backend-infra-engineer: Post v0.3.9-hotfix7 snapshot (build cleanup)
This commit is contained in:
50
docs/public/reference/SAVE_STATE_FORMAT.md
Normal file
50
docs/public/reference/SAVE_STATE_FORMAT.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Save State Format (v2)
|
||||
|
||||
This documents the chunked save-state format introduced in `Snes::saveState/loadState` (state file version 2).
|
||||
|
||||
## Goals
|
||||
- Forward/backward compatible: per-file header + per-chunk versioning.
|
||||
- Corruption-resilient: CRC on each chunk; size caps to avoid runaway allocations.
|
||||
- Explicit layout: avoid raw struct dumps to sidestep padding/endianness issues.
|
||||
|
||||
## File Structure
|
||||
```
|
||||
uint32 magic = 'YAZE' (0x59415A45)
|
||||
uint32 version = 2
|
||||
repeat until EOF:
|
||||
Chunk {
|
||||
uint32 tag // ASCII packed: 'SNES', 'CPU ', 'PPU ', 'APU '
|
||||
uint32 version // per-chunk version; currently 1 for all
|
||||
uint32 size // payload size in bytes (capped at 16 MiB)
|
||||
uint32 crc32 // CRC-32 of payload
|
||||
uint8 payload[size]
|
||||
}
|
||||
```
|
||||
|
||||
## Chunk Payloads (v1)
|
||||
- `SNES`: Core machine state (WRAM, timers, IRQ/NMI flags, latched ports, timers, etc.).
|
||||
- `CPU `: CPU registers/flags + breakpoint list (capped to 1024 entries).
|
||||
- `PPU `: PPU registers, VRAM/CGRAM/OAM, layer/window/bg structs written field-by-field.
|
||||
- `APU `: APU registers, ports, timers, and 64K ARAM (capped) plus DSP/SPC700 state.
|
||||
|
||||
## Compatibility
|
||||
- Legacy v1 flat saves (no magic) are still loadable: the loader falls back if the magic/version header is missing. They do not carry CRCs and remain best-effort only.
|
||||
- Host endianness: serialization assumes little-endian hosts; load/save will fail fast otherwise.
|
||||
|
||||
## Validation & Errors
|
||||
- Size guard: any chunk `size > 16 MiB` is rejected.
|
||||
- CRC guard: mismatched CRC rejects the load to avoid partial/dirty state.
|
||||
- Missing required chunks (`SNES`, `CPU `, `PPU `, `APU `) rejects the load.
|
||||
- Streams are checked for `fail()` after every read/write; callers receive `absl::Status`.
|
||||
|
||||
## Extending
|
||||
- Add a new chunk tag and bump its per-chunk `version` only. Keep `file version` stable unless the top-level format changes.
|
||||
- Keep payloads explicit (no raw struct dumps). Write scalars/arrays with defined width and order.
|
||||
- If you add new fields to an existing chunk, prefer:
|
||||
1. Extending the payload and bumping that chunk’s version.
|
||||
2. Keeping old fields first so older loaders can short-circuit safely.
|
||||
|
||||
## Conventions
|
||||
- Tags use little-endian ASCII packing: `'SNES'` -> `0x53454E53`.
|
||||
- CRC uses `render::CalculateCRC32`.
|
||||
- Max buffer cap mirrors the largest expected subsystem payload (WRAM/ARAM).
|
||||
140
docs/public/reference/SNES_COMPRESSION.md
Normal file
140
docs/public/reference/SNES_COMPRESSION.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# SNES Compression Format (ALttP)
|
||||
|
||||
Decompression algorithm used in A Link to the Past, documented from ZSpriteMaker.
|
||||
|
||||
**Source:** `~/Documents/Zelda/Editors/ZSpriteMaker-1/ZSpriteMaker/Utils.cs`
|
||||
|
||||
## Command Format
|
||||
|
||||
Each compressed block starts with a command byte:
|
||||
|
||||
```
|
||||
Normal Command (when upper 3 bits != 0b111):
|
||||
┌─────────────────────────────────────┐
|
||||
│ 7 6 5 │ 4 3 2 1 0 │
|
||||
│ CMD │ LENGTH (0-31) │
|
||||
└─────────────────────────────────────┘
|
||||
Length = (byte & 0x1F) + 1
|
||||
|
||||
Expanded Command (when upper 3 bits == 0b111):
|
||||
┌─────────────────────────────────────┬──────────────┐
|
||||
│ 7 6 5 │ 4 3 2 │ 1 0 │ Byte 2 │
|
||||
│ 0b111 │ CMD │ LENGTH_HI │ LENGTH_LO │
|
||||
└─────────────────────────────────────┴──────────────┘
|
||||
Length = ((byte & 0x03) << 8 | nextByte) + 1
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
| CMD | Name | Description |
|
||||
|-----|------|-------------|
|
||||
| 0 | Direct Copy | Copy `length` bytes directly from ROM to output |
|
||||
| 1 | Byte Fill | Repeat single byte `length` times |
|
||||
| 2 | Word Fill | Repeat 2-byte word `length/2` times |
|
||||
| 3 | Increasing Fill | Write byte, increment, repeat `length` times |
|
||||
| 4 | Repeat | Copy `length` bytes from earlier in output buffer |
|
||||
|
||||
## Terminator
|
||||
|
||||
`0xFF` byte terminates decompression.
|
||||
|
||||
## C++ Implementation
|
||||
|
||||
```cpp
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
std::vector<uint8_t> decompress_alttp(const uint8_t* rom, int pos) {
|
||||
std::vector<uint8_t> buffer(0x1000, 0);
|
||||
int bufferPos = 0;
|
||||
|
||||
while (true) {
|
||||
uint8_t databyte = rom[pos];
|
||||
if (databyte == 0xFF) break; // End marker
|
||||
|
||||
uint8_t cmd;
|
||||
int length;
|
||||
|
||||
if ((databyte & 0xE0) == 0xE0) {
|
||||
// Expanded command
|
||||
cmd = (databyte >> 2) & 0x07;
|
||||
length = ((databyte & 0x03) << 8) | rom[pos + 1];
|
||||
pos += 2;
|
||||
} else {
|
||||
// Normal command
|
||||
cmd = (databyte >> 5) & 0x07;
|
||||
length = databyte & 0x1F;
|
||||
pos += 1;
|
||||
}
|
||||
length += 1; // Minimum length is 1
|
||||
|
||||
switch (cmd) {
|
||||
case 0: // Direct Copy
|
||||
for (int i = 0; i < length; i++) {
|
||||
buffer[bufferPos++] = rom[pos++];
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // Byte Fill
|
||||
for (int i = 0; i < length; i++) {
|
||||
buffer[bufferPos++] = rom[pos];
|
||||
}
|
||||
pos += 1;
|
||||
break;
|
||||
|
||||
case 2: // Word Fill
|
||||
for (int i = 0; i < length; i += 2) {
|
||||
buffer[bufferPos++] = rom[pos];
|
||||
buffer[bufferPos++] = rom[pos + 1];
|
||||
}
|
||||
pos += 2;
|
||||
break;
|
||||
|
||||
case 3: // Increasing Fill
|
||||
{
|
||||
uint8_t val = rom[pos];
|
||||
for (int i = 0; i < length; i++) {
|
||||
buffer[bufferPos++] = val++;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4: // Repeat from buffer
|
||||
{
|
||||
// Little-endian address
|
||||
int addr = rom[pos] | (rom[pos + 1] << 8);
|
||||
for (int i = 0; i < length; i++) {
|
||||
buffer[bufferPos++] = buffer[addr++];
|
||||
}
|
||||
pos += 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.resize(bufferPos);
|
||||
return buffer;
|
||||
}
|
||||
```
|
||||
|
||||
## Address Conversion
|
||||
|
||||
```cpp
|
||||
// SNES LoROM to PC file offset
|
||||
inline int snes_to_pc(int addr) {
|
||||
return (addr & 0x7FFF) | ((addr & 0x7F0000) >> 1);
|
||||
}
|
||||
|
||||
// PC file offset to SNES LoROM
|
||||
inline int pc_to_snes(int addr) {
|
||||
return (addr & 0x7FFF) | 0x8000 | ((addr & 0x7F8000) << 1);
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Buffer size is 0x1000 (4KB) - typical for tile/map data
|
||||
- Command 4 (Repeat) uses little-endian address within output buffer
|
||||
- Expanded commands allow lengths up to 1024 bytes
|
||||
- Used for: tile graphics, tilemaps, some sprite data
|
||||
189
docs/public/reference/SNES_GRAPHICS.md
Normal file
189
docs/public/reference/SNES_GRAPHICS.md
Normal file
@@ -0,0 +1,189 @@
|
||||
# SNES Graphics Conversion
|
||||
|
||||
Tile format conversion routines for ALttP graphics, documented from ZSpriteMaker.
|
||||
|
||||
**Source:** `~/Documents/Zelda/Editors/ZSpriteMaker-1/ZSpriteMaker/Utils.cs`
|
||||
|
||||
## SNES Tile Formats
|
||||
|
||||
### 3BPP (3 bits per pixel, 8 colors)
|
||||
- Used for: Sprites, some backgrounds
|
||||
- 24 bytes per 8x8 tile
|
||||
- Planar format: 2 bytes per row (planes 0-1) + 1 byte per row (plane 2)
|
||||
|
||||
### 4BPP (4 bits per pixel, 16 colors)
|
||||
- Used for: Most backgrounds, UI
|
||||
- 32 bytes per 8x8 tile
|
||||
- Planar format: 2 interleaved bitplanes per 16 bytes
|
||||
|
||||
## 3BPP Tile Layout
|
||||
|
||||
```
|
||||
Bytes 0-15: Planes 0 and 1 (interleaved, 2 bytes per row)
|
||||
Bytes 16-23: Plane 2 (1 byte per row)
|
||||
|
||||
Row 0: [Plane0_Row0][Plane1_Row0]
|
||||
Row 1: [Plane0_Row1][Plane1_Row1]
|
||||
...
|
||||
Row 7: [Plane0_Row7][Plane1_Row7]
|
||||
[Plane2_Row0][Plane2_Row1]...[Plane2_Row7]
|
||||
```
|
||||
|
||||
## C++ Implementation: 3BPP to 8BPP
|
||||
|
||||
Converts a sheet of 64 tiles (16x4 arrangement, 128x32 pixels) from 3BPP to indexed 8BPP:
|
||||
|
||||
```cpp
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
constexpr std::array<uint8_t, 8> bitmask = {
|
||||
0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
|
||||
};
|
||||
|
||||
// Input: 24 * 64 = 1536 bytes (64 tiles in 3BPP)
|
||||
// Output: 128 * 32 = 4096 bytes (8BPP indexed)
|
||||
std::array<uint8_t, 0x1000> snes_3bpp_to_8bpp(const uint8_t* data) {
|
||||
std::array<uint8_t, 0x1000> sheet{};
|
||||
int index = 0;
|
||||
|
||||
for (int tileRow = 0; tileRow < 4; tileRow++) { // 4 rows of tiles
|
||||
for (int tileCol = 0; tileCol < 16; tileCol++) { // 16 tiles per row
|
||||
int tileOffset = (tileCol + tileRow * 16) * 24; // 24 bytes per tile
|
||||
|
||||
for (int y = 0; y < 8; y++) { // 8 pixel rows
|
||||
uint8_t plane0 = data[tileOffset + y * 2];
|
||||
uint8_t plane1 = data[tileOffset + y * 2 + 1];
|
||||
uint8_t plane2 = data[tileOffset + 16 + y];
|
||||
|
||||
for (int x = 0; x < 8; x++) { // 8 pixels per row
|
||||
uint8_t mask = bitmask[x];
|
||||
uint8_t pixel = 0;
|
||||
|
||||
if (plane0 & mask) pixel |= 1;
|
||||
if (plane1 & mask) pixel |= 2;
|
||||
if (plane2 & mask) pixel |= 4;
|
||||
|
||||
// Calculate output position in 128-wide sheet
|
||||
int outX = tileCol * 8 + x;
|
||||
int outY = tileRow * 8 + y;
|
||||
sheet[outY * 128 + outX] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sheet;
|
||||
}
|
||||
```
|
||||
|
||||
## Alternative: Direct Index Calculation
|
||||
|
||||
```cpp
|
||||
std::array<uint8_t, 0x1000> snes_3bpp_to_8bpp_v2(const uint8_t* data) {
|
||||
std::array<uint8_t, 0x1000> sheet{};
|
||||
int index = 0;
|
||||
|
||||
for (int j = 0; j < 4; j++) { // Tile row
|
||||
for (int i = 0; i < 16; i++) { // Tile column
|
||||
for (int y = 0; y < 8; y++) { // Pixel row
|
||||
int base = y * 2 + i * 24 + j * 384;
|
||||
|
||||
uint8_t line0 = data[base];
|
||||
uint8_t line1 = data[base + 1];
|
||||
uint8_t line2 = data[base - y * 2 + y + 16];
|
||||
|
||||
for (uint8_t mask = 0x80; mask > 0; mask >>= 1) {
|
||||
uint8_t pixel = 0;
|
||||
if (line0 & mask) pixel |= 1;
|
||||
if (line1 & mask) pixel |= 2;
|
||||
if (line2 & mask) pixel |= 4;
|
||||
sheet[index++] = pixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sheet;
|
||||
}
|
||||
```
|
||||
|
||||
## Palette Reading
|
||||
|
||||
SNES uses 15-bit BGR color (5 bits per channel):
|
||||
|
||||
```cpp
|
||||
#include <cstdint>
|
||||
|
||||
struct Color {
|
||||
uint8_t r, g, b, a;
|
||||
};
|
||||
|
||||
Color read_snes_color(const uint8_t* data, int offset) {
|
||||
uint16_t color = data[offset] | (data[offset + 1] << 8);
|
||||
|
||||
return {
|
||||
static_cast<uint8_t>((color & 0x001F) << 3), // R: bits 0-4
|
||||
static_cast<uint8_t>(((color >> 5) & 0x1F) << 3), // G: bits 5-9
|
||||
static_cast<uint8_t>(((color >> 10) & 0x1F) << 3), // B: bits 10-14
|
||||
255 // A: opaque
|
||||
};
|
||||
}
|
||||
|
||||
// Read full 8-color palette (3BPP)
|
||||
std::array<Color, 8> read_3bpp_palette(const uint8_t* data, int offset) {
|
||||
std::array<Color, 8> palette;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
palette[i] = read_snes_color(data, offset + i * 2);
|
||||
}
|
||||
return palette;
|
||||
}
|
||||
|
||||
// Read full 16-color palette (4BPP)
|
||||
std::array<Color, 16> read_4bpp_palette(const uint8_t* data, int offset) {
|
||||
std::array<Color, 16> palette;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
palette[i] = read_snes_color(data, offset + i * 2);
|
||||
}
|
||||
return palette;
|
||||
}
|
||||
```
|
||||
|
||||
## OAM Tile Positioning
|
||||
|
||||
From ZSpriteMaker's OamTile class - convert tile ID to sheet coordinates:
|
||||
|
||||
```cpp
|
||||
// Tile ID to sprite sheet pixel position
|
||||
// Assumes 16 tiles per row (128 pixels wide sheet)
|
||||
inline int tile_to_sheet_x(uint16_t id) { return (id % 16) * 8; }
|
||||
inline int tile_to_sheet_y(uint16_t id) { return (id / 16) * 8; }
|
||||
|
||||
// Packed OAM format (32-bit)
|
||||
inline uint32_t pack_oam_tile(uint16_t id, uint8_t x, uint8_t y,
|
||||
uint8_t palette, uint8_t priority,
|
||||
bool mirrorX, bool mirrorY) {
|
||||
return (id << 16) |
|
||||
((mirrorY ? 0 : 1) << 31) |
|
||||
((mirrorX ? 0 : 1) << 30) |
|
||||
(priority << 28) |
|
||||
(palette << 25) |
|
||||
(x << 8) |
|
||||
y;
|
||||
}
|
||||
```
|
||||
|
||||
## Sheet Dimensions
|
||||
|
||||
| Format | Tiles | Sheet Size | Bytes/Tile | Total Bytes |
|
||||
|--------|-------|------------|------------|-------------|
|
||||
| 3BPP 64-tile | 16x4 | 128x32 px | 24 | 1,536 |
|
||||
| 4BPP 64-tile | 16x4 | 128x32 px | 32 | 2,048 |
|
||||
| 3BPP 128-tile | 16x8 | 128x64 px | 24 | 3,072 |
|
||||
|
||||
## Integration Notes
|
||||
|
||||
- Color index 0 is typically transparent
|
||||
- SNES sprites use 3BPP (8 colors per palette row)
|
||||
- Background tiles often use 4BPP (16 colors)
|
||||
- ALttP Link sprites: 3BPP, multiple sheets for different states
|
||||
209
docs/public/reference/SYMBOL_FORMAT.md
Normal file
209
docs/public/reference/SYMBOL_FORMAT.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# ALttP Symbol File Format
|
||||
|
||||
Documentation for importing disassembly symbol files into YAZE.
|
||||
|
||||
**Source:** `~/Code/alttp-gigaleak/DISASM/jpdasm/symbols_*.asm`
|
||||
|
||||
## Available Symbol Files
|
||||
|
||||
| File | Contents | Address Range |
|
||||
|------|----------|---------------|
|
||||
| `symbols_wram.asm` | Work RAM labels | $7E0000-$7FFFFF |
|
||||
| `symbols_sram.asm` | Save RAM labels | $700000-$70FFFF |
|
||||
| `symbols_apu.asm` | Audio processor | APU addresses |
|
||||
| `registers.asm` | Hardware registers | $2100-$21FF, $4200-$43FF |
|
||||
|
||||
## File Format
|
||||
|
||||
Simple assembly-style symbol definitions:
|
||||
|
||||
```asm
|
||||
; Comment lines start with semicolon
|
||||
SYMBOL_NAME = $AABBCC ; Optional inline comment
|
||||
|
||||
; Multi-line comments for documentation
|
||||
; LENGTH: 0x10
|
||||
BLOCK_START = $7E0000
|
||||
```
|
||||
|
||||
## Symbol Naming Conventions
|
||||
|
||||
### Suffixes
|
||||
| Suffix | Meaning |
|
||||
|--------|---------|
|
||||
| `L` | Low byte of 16-bit value |
|
||||
| `H` | High byte of 16-bit value |
|
||||
| `U` | Unused high byte |
|
||||
| `Q` | Queue (for NMI updates) |
|
||||
|
||||
### Prefixes
|
||||
| Prefix | Category |
|
||||
|--------|----------|
|
||||
| `LINK_` | Player state |
|
||||
| `SPR_` | Sprite/enemy |
|
||||
| `NMI_` | NMI handler |
|
||||
| `OAM_` | OAM buffer |
|
||||
| `UNUSED_` | Free RAM |
|
||||
|
||||
### Bitfield Documentation
|
||||
```asm
|
||||
; a - found to the north west
|
||||
; b - found to the north east
|
||||
; c - found to the south west
|
||||
; d - found to the south east
|
||||
; Bitfield: abcd....
|
||||
TILE_DIRECTION = $7E000A
|
||||
```
|
||||
|
||||
## Key Symbols (Quick Reference)
|
||||
|
||||
### Game State ($7E0010-$7E001F)
|
||||
```asm
|
||||
MODE = $7E0010 ; Main game mode
|
||||
SUBMODE = $7E0011 ; Sub-mode within mode
|
||||
LAG = $7E0012 ; NMI sync flag
|
||||
INIDISPQ = $7E0013 ; Display brightness queue
|
||||
NMISTRIPES = $7E0014 ; Tilemap update flag
|
||||
NMICGRAM = $7E0015 ; Palette update flag
|
||||
```
|
||||
|
||||
### Player Position (typical)
|
||||
```asm
|
||||
LINK_X_LO = $7E0022 ; Link X position (low)
|
||||
LINK_X_HI = $7E0023 ; Link X position (high)
|
||||
LINK_Y_LO = $7E0020 ; Link Y position (low)
|
||||
LINK_Y_HI = $7E0021 ; Link Y position (high)
|
||||
LINK_LAYER = $7E00EE ; Current layer (0=BG1, 1=BG2)
|
||||
```
|
||||
|
||||
### Room/Dungeon
|
||||
```asm
|
||||
ROOM_ID = $7E00A0 ; Current room number
|
||||
DUNGEON_ID = $7E040C ; Current dungeon
|
||||
```
|
||||
|
||||
## C++ Parser
|
||||
|
||||
```cpp
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
|
||||
struct Symbol {
|
||||
std::string name;
|
||||
uint32_t address;
|
||||
std::string comment;
|
||||
};
|
||||
|
||||
std::unordered_map<uint32_t, Symbol> parse_symbols(const std::string& path) {
|
||||
std::unordered_map<uint32_t, Symbol> symbols;
|
||||
std::ifstream file(path);
|
||||
std::string line;
|
||||
|
||||
// Pattern: NAME = $ADDRESS ; comment
|
||||
std::regex pattern(R"(^(\w+)\s*=\s*\$([0-9A-Fa-f]+)\s*(?:;\s*(.*))?$)");
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
// Skip pure comment lines
|
||||
if (line.empty() || line[0] == ';') continue;
|
||||
|
||||
std::smatch match;
|
||||
if (std::regex_search(line, match, pattern)) {
|
||||
Symbol sym;
|
||||
sym.name = match[1].str();
|
||||
sym.address = std::stoul(match[2].str(), nullptr, 16);
|
||||
sym.comment = match[3].str();
|
||||
|
||||
symbols[sym.address] = sym;
|
||||
}
|
||||
}
|
||||
|
||||
return symbols;
|
||||
}
|
||||
|
||||
// Get symbol name for address (returns empty if not found)
|
||||
std::string lookup_symbol(const std::unordered_map<uint32_t, Symbol>& syms,
|
||||
uint32_t addr) {
|
||||
auto it = syms.find(addr);
|
||||
return (it != syms.end()) ? it->second.name : "";
|
||||
}
|
||||
```
|
||||
|
||||
## Integration Ideas
|
||||
|
||||
### Hex Editor Enhancement
|
||||
```cpp
|
||||
// Display symbol alongside address
|
||||
void draw_hex_line(uint32_t addr, const uint8_t* data) {
|
||||
std::string sym = lookup_symbol(symbols, addr);
|
||||
if (!sym.empty()) {
|
||||
printf("%06X %-20s ", addr, sym.c_str());
|
||||
} else {
|
||||
printf("%06X %-20s ", addr, "");
|
||||
}
|
||||
// ... draw hex bytes
|
||||
}
|
||||
```
|
||||
|
||||
### Disassembly View
|
||||
```cpp
|
||||
// Replace addresses with symbols in ASM output
|
||||
std::string format_operand(uint32_t addr) {
|
||||
std::string sym = lookup_symbol(symbols, addr);
|
||||
if (!sym.empty()) {
|
||||
return sym;
|
||||
}
|
||||
return "$" + to_hex(addr);
|
||||
}
|
||||
```
|
||||
|
||||
### Memory Watcher
|
||||
```cpp
|
||||
struct Watch {
|
||||
std::string label;
|
||||
uint32_t address;
|
||||
uint8_t size; // 1, 2, or 3 bytes
|
||||
};
|
||||
|
||||
// Auto-populate watches from symbol file
|
||||
std::vector<Watch> create_watches_from_symbols() {
|
||||
std::vector<Watch> watches;
|
||||
|
||||
// Key game state
|
||||
watches.push_back({"Mode", 0x7E0010, 1});
|
||||
watches.push_back({"Submode", 0x7E0011, 1});
|
||||
watches.push_back({"Link X", 0x7E0022, 2});
|
||||
watches.push_back({"Link Y", 0x7E0020, 2});
|
||||
watches.push_back({"Room", 0x7E00A0, 2});
|
||||
|
||||
return watches;
|
||||
}
|
||||
```
|
||||
|
||||
## Free RAM Discovery
|
||||
|
||||
Symbol files mark unused RAM:
|
||||
```asm
|
||||
; FREE RAM: 0x20
|
||||
UNUSED_7E0500 = $7E0500
|
||||
UNUSED_7E0501 = $7E0501
|
||||
; ...
|
||||
|
||||
; BIG FREE RAM
|
||||
UNUSED_7E1000 = $7E1000 ; Large block available
|
||||
```
|
||||
|
||||
Parse for `FREE RAM` comments to find available space for ROM hacks.
|
||||
|
||||
## File Locations
|
||||
|
||||
```
|
||||
~/Code/alttp-gigaleak/DISASM/jpdasm/
|
||||
├── symbols_wram.asm # Work RAM ($7E)
|
||||
├── symbols_sram.asm # Save RAM ($70)
|
||||
├── symbols_apu.asm # Audio
|
||||
├── registers.asm # Hardware registers
|
||||
└── registers_spc.asm # SPC700 registers
|
||||
```
|
||||
213
docs/public/reference/ZSM_FORMAT.md
Normal file
213
docs/public/reference/ZSM_FORMAT.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# ZSM File Format Specification
|
||||
|
||||
ZSpriteMaker Project File format (`.zsm`) - used by ZSpriteMaker for ALttP custom sprites.
|
||||
|
||||
**Source:** `~/Documents/Zelda/Editors/ZSpriteMaker-1/`
|
||||
|
||||
## Format Overview
|
||||
|
||||
Binary file format using .NET BinaryWriter/BinaryReader conventions:
|
||||
- Strings: Length-prefixed (7-bit encoded length + UTF-8 bytes)
|
||||
- Integers: Little-endian 32-bit
|
||||
- Booleans: Single byte (0x00 = false, 0x01 = true)
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ ANIMATIONS SECTION │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ int32 animationCount │
|
||||
│ for each animation: │
|
||||
│ string name (length-prefixed) │
|
||||
│ byte frameStart │
|
||||
│ byte frameEnd │
|
||||
│ byte frameSpeed │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ FRAMES SECTION │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ int32 frameCount │
|
||||
│ for each frame: │
|
||||
│ int32 tileCount │
|
||||
│ for each tile: │
|
||||
│ uint16 id (tile index in sheet) │
|
||||
│ byte palette (0-7) │
|
||||
│ bool mirrorX │
|
||||
│ bool mirrorY │
|
||||
│ byte priority (0-3, default 3) │
|
||||
│ bool size (false=8x8, true=16x16) │
|
||||
│ byte x (0-251) │
|
||||
│ byte y (0-219) │
|
||||
│ byte z (depth/layer) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ SPRITE PROPERTIES (20 booleans) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ bool blockable │
|
||||
│ bool canFall │
|
||||
│ bool collisionLayer │
|
||||
│ bool customDeath │
|
||||
│ bool damageSound │
|
||||
│ bool deflectArrows │
|
||||
│ bool deflectProjectiles │
|
||||
│ bool fast │
|
||||
│ bool harmless │
|
||||
│ bool impervious │
|
||||
│ bool imperviousArrow │
|
||||
│ bool imperviousMelee │
|
||||
│ bool interaction │
|
||||
│ bool isBoss │
|
||||
│ bool persist │
|
||||
│ bool shadow │
|
||||
│ bool smallShadow │
|
||||
│ statis (stasis) │
|
||||
│ bool statue │
|
||||
│ bool waterSprite │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ SPRITE STATS (6 bytes) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ byte prize (drop item ID) │
|
||||
│ byte palette (sprite palette) │
|
||||
│ byte oamNbr (OAM slot count) │
|
||||
│ byte hitbox (collision box ID) │
|
||||
│ byte health │
|
||||
│ byte damage │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ USER ROUTINES SECTION (optional) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ string spriteName (length-prefixed) │
|
||||
│ int32 routineCount │
|
||||
│ for each routine: │
|
||||
│ string name (e.g., "Long Main") │
|
||||
│ string code (ASM code) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ SPRITE ID (optional) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ string spriteId (hex string) │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Types
|
||||
|
||||
### OamTile
|
||||
```cpp
|
||||
struct OamTile {
|
||||
uint16_t id; // Tile index in sprite sheet (0-511)
|
||||
uint8_t palette; // Palette index (0-7)
|
||||
bool mirrorX; // Horizontal flip
|
||||
bool mirrorY; // Vertical flip
|
||||
uint8_t priority; // BG priority (0-3)
|
||||
bool size; // false=8x8, true=16x16
|
||||
uint8_t x; // X position (0-251)
|
||||
uint8_t y; // Y position (0-219)
|
||||
uint8_t z; // Z depth for sorting
|
||||
};
|
||||
|
||||
// Tile sheet position from ID:
|
||||
// sheet_x = (id % 16) * 8
|
||||
// sheet_y = (id / 16) * 8
|
||||
```
|
||||
|
||||
### AnimationGroup
|
||||
```cpp
|
||||
struct AnimationGroup {
|
||||
std::string name; // Animation name
|
||||
uint8_t frameStart; // First frame index
|
||||
uint8_t frameEnd; // Last frame index
|
||||
uint8_t frameSpeed; // Frames per tick
|
||||
};
|
||||
```
|
||||
|
||||
### Frame
|
||||
```cpp
|
||||
struct Frame {
|
||||
std::vector<OamTile> tiles;
|
||||
};
|
||||
```
|
||||
|
||||
## Sprite Properties Bitfield (Alternative)
|
||||
|
||||
The 20 boolean properties could be packed as bitfield:
|
||||
```cpp
|
||||
enum SpriteFlags : uint32_t {
|
||||
BLOCKABLE = 1 << 0,
|
||||
CAN_FALL = 1 << 1,
|
||||
COLLISION_LAYER = 1 << 2,
|
||||
CUSTOM_DEATH = 1 << 3,
|
||||
DAMAGE_SOUND = 1 << 4,
|
||||
DEFLECT_ARROWS = 1 << 5,
|
||||
DEFLECT_PROJECTILES = 1 << 6,
|
||||
FAST = 1 << 7,
|
||||
HARMLESS = 1 << 8,
|
||||
IMPERVIOUS = 1 << 9,
|
||||
IMPERVIOUS_ARROW = 1 << 10,
|
||||
IMPERVIOUS_MELEE = 1 << 11,
|
||||
INTERACTION = 1 << 12,
|
||||
IS_BOSS = 1 << 13,
|
||||
PERSIST = 1 << 14,
|
||||
SHADOW = 1 << 15,
|
||||
SMALL_SHADOW = 1 << 16,
|
||||
STASIS = 1 << 17,
|
||||
STATUE = 1 << 18,
|
||||
WATER_SPRITE = 1 << 19,
|
||||
};
|
||||
```
|
||||
|
||||
## Default User Routines
|
||||
|
||||
New projects include three template routines:
|
||||
1. **Long Main** - Main sprite loop (`TemplateLongMain.asm`)
|
||||
2. **Sprite Prep** - Initialization (`TemplatePrep.asm`)
|
||||
3. **Sprite Draw** - Rendering (`TemplateDraw.asm`)
|
||||
|
||||
## Related Formats
|
||||
|
||||
### ZSPR (ALttP Randomizer Format)
|
||||
Different format used by ALttP Randomizer for Link sprite replacements.
|
||||
- Magic: `ZSPR` (4 bytes)
|
||||
- Contains: sprite sheet, palette, glove colors, metadata
|
||||
- Spec: https://github.com/Zarby89/ZScreamRandomizer
|
||||
|
||||
### ZSM vs ZSPR
|
||||
| Feature | ZSM | ZSPR |
|
||||
|---------|-----|------|
|
||||
| Purpose | Custom enemy/NPC sprites | Link sprite replacement |
|
||||
| Contains ASM | Yes (routines) | No |
|
||||
| Animation data | Yes | No (uses ROM animations) |
|
||||
| Properties | Sprite behavior flags | Metadata only |
|
||||
| Editor | ZSpriteMaker | SpriteSomething, others |
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Reading ZSM in C++
|
||||
```cpp
|
||||
// .NET BinaryReader string format:
|
||||
std::string read_dotnet_string(std::istream& is) {
|
||||
uint32_t length = 0;
|
||||
uint8_t byte;
|
||||
int shift = 0;
|
||||
do {
|
||||
is.read(reinterpret_cast<char*>(&byte), 1);
|
||||
length |= (byte & 0x7F) << shift;
|
||||
shift += 7;
|
||||
} while (byte & 0x80);
|
||||
|
||||
std::string result(length, '\0');
|
||||
is.read(&result[0], length);
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Validation
|
||||
- Frame count typically 0-255 (byte range in UI)
|
||||
- Tile positions clamped: x < 252, y < 220
|
||||
- Palette 0-7
|
||||
- Priority 0-3
|
||||
|
||||
## Source Files
|
||||
|
||||
From `~/Documents/Zelda/Editors/ZSpriteMaker-1/ZSpriteMaker/`:
|
||||
- `MainWindow.xaml.cs:323-419` - Save_Command (write format)
|
||||
- `MainWindow.xaml.cs:209-319` - Open_Command (read format)
|
||||
- `OamTile.cs` - Tile data structure
|
||||
- `Frame.cs` - Frame container
|
||||
- `AnimationGroup.cs` - Animation definition
|
||||
@@ -1,5 +1,48 @@
|
||||
# Changelog
|
||||
|
||||
## 0.4.1 (December 2025)
|
||||
|
||||
### Overworld Editor Fixes
|
||||
|
||||
**Vanilla ROM Corruption Fix**:
|
||||
- Fixed critical bug where save functions wrote to custom ASM address space (0x140000+) for ALL ROMs without version checking
|
||||
- `SaveAreaSpecificBGColors()`, `SaveCustomOverworldASM()`, and `SaveDiggableTiles()` now properly check ROM version before writing
|
||||
- Vanilla and v1 ROMs are no longer corrupted by writes to ZSCustomOverworld address space
|
||||
- Added `OverworldVersionHelper` with `SupportsCustomBGColors()` and `SupportsAreaEnum()` methods
|
||||
|
||||
**Toolbar UI Improvements**:
|
||||
- Increased button widths from 30px to 40px for comfortable touch targets
|
||||
- Added version badge showing "Vanilla", "v2", or "v3" ROM version with color coding
|
||||
- Added "Upgrade" button for applying ZSCustomOverworld ASM patch to vanilla ROMs
|
||||
- Improved panel toggle button spacing and column layout
|
||||
|
||||
### Testing Infrastructure
|
||||
|
||||
**ROM Auto-Discovery**:
|
||||
- Tests now automatically discover ROMs in common locations (roms/, ../roms/, etc.)
|
||||
- Searches for common filenames: zelda3.sfc, alttp_vanilla.sfc, vanilla.sfc
|
||||
- Environment variable `YAZE_TEST_ROM_PATH` still takes precedence if set
|
||||
|
||||
**Overworld Regression Tests**:
|
||||
- Added 9 new regression tests for save function version checks
|
||||
- Tests verify vanilla/v1/v2/v3 ROM handling for all version-gated save functions
|
||||
- Version feature matrix validation tests added
|
||||
|
||||
### Logging & Diagnostics
|
||||
- Added CLI controls for log level/categories and console mirroring (`--log_level`, `--log_categories`, `--log_to_console`); `--debug` now force-enables console logging at debug level.
|
||||
- Startup logging now reports the resolved level, categories, and log file destination for easier reproducibility.
|
||||
|
||||
### Editor & Panel Launch Controls
|
||||
- `--open_panels` matching is case-insensitive and accepts both display names and stable panel IDs (e.g., `dungeon.room_list`, `Room 105`, `welcome`, `dashboard`).
|
||||
- New startup visibility overrides (`--startup_welcome`, `--startup_dashboard`, `--startup_sidebar`) let you force panels to show/hide on launch for automation or demos.
|
||||
- Welcome and dashboard behavior is coordinated through the UI layer so CLI overrides and in-app toggles stay in sync.
|
||||
|
||||
### Documentation & Testing
|
||||
- Debugging guides refreshed with the new logging filters and startup panel controls.
|
||||
- Startup flag reference and dungeon editor guide now use panel terminology and up-to-date CLI examples for automation setups.
|
||||
|
||||
---
|
||||
|
||||
## 0.3.9 (November 2025)
|
||||
|
||||
### AI Agent Infrastructure
|
||||
|
||||
@@ -257,5 +257,17 @@ Tile Data:
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: October 13, 2025
|
||||
## Additional Format Documentation
|
||||
|
||||
For more detailed format specifications, see:
|
||||
|
||||
- [SNES Graphics Format](SNES_GRAPHICS.md) - Tile and sprite format specifications
|
||||
- [SNES Compression](SNES_COMPRESSION.md) - Detailed LC-LZ2 compression algorithm
|
||||
- [Symbol Format](SYMBOL_FORMAT.md) - Assembler symbol file format
|
||||
- [ZSM Format](ZSM_FORMAT.md) - Music and sound effect format
|
||||
- [Save State Format](SAVE_STATE_FORMAT.md) - Emulator save state specifications
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: November 27, 2025
|
||||
|
||||
|
||||
659
docs/public/reference/z3ed-command-reference.md
Normal file
659
docs/public/reference/z3ed-command-reference.md
Normal file
@@ -0,0 +1,659 @@
|
||||
# Z3ED Command Reference
|
||||
|
||||
Complete command reference for the z3ed CLI tool, including all automation and AI-powered features.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [ROM Operations](#rom-operations)
|
||||
2. [Editor Automation](#editor-automation)
|
||||
3. [Testing Commands](#testing-commands)
|
||||
4. [Build & CI/CD](#build--cicd)
|
||||
5. [Query & Discovery](#query--discovery)
|
||||
6. [Interactive REPL](#interactive-repl)
|
||||
7. [Network & Collaboration](#network--collaboration)
|
||||
8. [AI Integration](#ai-integration)
|
||||
|
||||
## ROM Operations
|
||||
|
||||
### `z3ed rom read`
|
||||
Read bytes from ROM at specified address.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed rom read --address <hex> [--length <bytes>] [--format hex|ascii|binary]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Read 16 bytes from address 0x1000
|
||||
z3ed rom read --address 0x1000 --length 16
|
||||
|
||||
# Read 256 bytes and display as ASCII
|
||||
z3ed rom read --address 0x20000 --length 256 --format ascii
|
||||
|
||||
# Read single byte
|
||||
z3ed rom read --address 0xFFFF
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"address": "0x001000",
|
||||
"data": "A9 00 85 2C A9 01 85 2D A9 02 85 2E A9 03 85 2F",
|
||||
"ascii": ".........,......",
|
||||
"length": 16
|
||||
}
|
||||
```
|
||||
|
||||
### `z3ed rom write`
|
||||
Write bytes to ROM at specified address.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed rom write --address <hex> --data <hex_string> [--verify]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Write 4 bytes
|
||||
z3ed rom write --address 0x1000 --data "A9 00 85 2C"
|
||||
|
||||
# Write with verification
|
||||
z3ed rom write --address 0x2000 --data "FF FE FD FC" --verify
|
||||
|
||||
# Write ASCII string (converted to hex)
|
||||
z3ed rom write --address 0x3000 --data "YAZE" --format ascii
|
||||
```
|
||||
|
||||
### `z3ed rom validate`
|
||||
Validate ROM integrity and structure.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed rom validate [--checksums] [--headers] [--regions] [--fix]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Full validation
|
||||
z3ed rom validate
|
||||
|
||||
# Validate checksums only
|
||||
z3ed rom validate --checksums
|
||||
|
||||
# Validate and attempt fixes
|
||||
z3ed rom validate --fix
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"valid": true,
|
||||
"checksums": {
|
||||
"header": "OK",
|
||||
"complement": "OK"
|
||||
},
|
||||
"headers": {
|
||||
"title": "THE LEGEND OF ZELDA",
|
||||
"version": "1.0",
|
||||
"region": "USA"
|
||||
},
|
||||
"issues": [],
|
||||
"warnings": ["Expanded ROM detected"]
|
||||
}
|
||||
```
|
||||
|
||||
### `z3ed rom snapshot`
|
||||
Create a named snapshot of current ROM state.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed rom snapshot --name <name> [--compress] [--metadata <json>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Create snapshot
|
||||
z3ed rom snapshot --name "before_dungeon_edit"
|
||||
|
||||
# Compressed snapshot with metadata
|
||||
z3ed rom snapshot --name "v1.0_release" --compress --metadata '{"version": "1.0", "author": "user"}'
|
||||
|
||||
# List snapshots
|
||||
z3ed rom list-snapshots
|
||||
```
|
||||
|
||||
### `z3ed rom restore`
|
||||
Restore ROM from a previous snapshot.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed rom restore --snapshot <name> [--verify]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Restore snapshot
|
||||
z3ed rom restore --snapshot "before_dungeon_edit"
|
||||
|
||||
# Restore with verification
|
||||
z3ed rom restore --snapshot "last_stable" --verify
|
||||
```
|
||||
|
||||
## Editor Automation
|
||||
|
||||
### `z3ed editor dungeon`
|
||||
Automate dungeon editor operations.
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
#### `place-object`
|
||||
Place an object in a dungeon room.
|
||||
|
||||
```bash
|
||||
z3ed editor dungeon place-object --room <id> --type <object_id> --x <x> --y <y> [--layer <0|1|2>]
|
||||
|
||||
# Example: Place a chest at position (10, 15) in room 0
|
||||
z3ed editor dungeon place-object --room 0 --type 0x22 --x 10 --y 15
|
||||
```
|
||||
|
||||
#### `set-property`
|
||||
Modify room properties.
|
||||
|
||||
```bash
|
||||
z3ed editor dungeon set-property --room <id> --property <name> --value <value>
|
||||
|
||||
# Example: Set room darkness
|
||||
z3ed editor dungeon set-property --room 5 --property "dark" --value true
|
||||
```
|
||||
|
||||
#### `list-objects`
|
||||
List all objects in a room.
|
||||
|
||||
```bash
|
||||
z3ed editor dungeon list-objects --room <id> [--filter-type <type>]
|
||||
|
||||
# Example: List all chests in room 10
|
||||
z3ed editor dungeon list-objects --room 10 --filter-type 0x22
|
||||
```
|
||||
|
||||
#### `validate-room`
|
||||
Check room for issues.
|
||||
|
||||
```bash
|
||||
z3ed editor dungeon validate-room --room <id> [--fix-issues]
|
||||
|
||||
# Example: Validate and fix room 0
|
||||
z3ed editor dungeon validate-room --room 0 --fix-issues
|
||||
```
|
||||
|
||||
### `z3ed editor overworld`
|
||||
Automate overworld editor operations.
|
||||
|
||||
**Subcommands:**
|
||||
|
||||
#### `set-tile`
|
||||
Modify a tile on the overworld map.
|
||||
|
||||
```bash
|
||||
z3ed editor overworld set-tile --map <id> --x <x> --y <y> --tile <tile_id>
|
||||
|
||||
# Example: Set grass tile at position (100, 50) on Light World
|
||||
z3ed editor overworld set-tile --map 0x00 --x 100 --y 50 --tile 0x002
|
||||
```
|
||||
|
||||
#### `place-entrance`
|
||||
Add an entrance to the overworld.
|
||||
|
||||
```bash
|
||||
z3ed editor overworld place-entrance --map <id> --x <x> --y <y> --target <room_id> [--type <type>]
|
||||
|
||||
# Example: Place dungeon entrance
|
||||
z3ed editor overworld place-entrance --map 0x00 --x 200 --y 150 --target 0x10 --type "dungeon"
|
||||
```
|
||||
|
||||
#### `modify-sprite`
|
||||
Modify sprite properties.
|
||||
|
||||
```bash
|
||||
z3ed editor overworld modify-sprite --map <id> --sprite-index <idx> --property <prop> --value <val>
|
||||
|
||||
# Example: Change enemy sprite type
|
||||
z3ed editor overworld modify-sprite --map 0x00 --sprite-index 0 --property "type" --value 0x08
|
||||
```
|
||||
|
||||
### `z3ed editor batch`
|
||||
Execute multiple editor operations from a script file.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed editor batch --script <file> [--dry-run] [--parallel] [--continue-on-error]
|
||||
```
|
||||
|
||||
**Script Format (JSON):**
|
||||
```json
|
||||
{
|
||||
"operations": [
|
||||
{
|
||||
"editor": "dungeon",
|
||||
"action": "place-object",
|
||||
"params": {
|
||||
"room": 1,
|
||||
"type": 34,
|
||||
"x": 10,
|
||||
"y": 15
|
||||
}
|
||||
},
|
||||
{
|
||||
"editor": "overworld",
|
||||
"action": "set-tile",
|
||||
"params": {
|
||||
"map": 0,
|
||||
"x": 20,
|
||||
"y": 30,
|
||||
"tile": 322
|
||||
}
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"stop_on_error": false,
|
||||
"validate_after": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Commands
|
||||
|
||||
### `z3ed test run`
|
||||
Execute test suites.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed test run [--category <unit|integration|e2e>] [--filter <pattern>] [--parallel]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Run all tests
|
||||
z3ed test run
|
||||
|
||||
# Run unit tests only
|
||||
z3ed test run --category unit
|
||||
|
||||
# Run specific test pattern
|
||||
z3ed test run --filter "*Overworld*"
|
||||
|
||||
# Parallel execution
|
||||
z3ed test run --parallel
|
||||
```
|
||||
|
||||
### `z3ed test generate`
|
||||
Generate test code for a class or component.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed test generate --target <class|file> --output <file> [--framework <gtest|catch2>] [--include-mocks]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Generate tests for a class
|
||||
z3ed test generate --target OverworldEditor --output overworld_test.cc
|
||||
|
||||
# Generate with mocks
|
||||
z3ed test generate --target DungeonRoom --output dungeon_test.cc --include-mocks
|
||||
|
||||
# Generate integration tests
|
||||
z3ed test generate --target src/app/rom.cc --output rom_integration_test.cc --framework catch2
|
||||
```
|
||||
|
||||
### `z3ed test record`
|
||||
Record interactions for test generation.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed test record --name <test_name> --start
|
||||
z3ed test record --stop [--save-as <file>]
|
||||
z3ed test record --pause
|
||||
z3ed test record --resume
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Start recording
|
||||
z3ed test record --name "dungeon_edit_test" --start
|
||||
|
||||
# Perform actions...
|
||||
z3ed editor dungeon place-object --room 0 --type 0x22 --x 10 --y 10
|
||||
|
||||
# Stop and save
|
||||
z3ed test record --stop --save-as dungeon_test.json
|
||||
|
||||
# Generate test from recording
|
||||
z3ed test generate --from-recording dungeon_test.json --output dungeon_test.cc
|
||||
```
|
||||
|
||||
### `z3ed test baseline`
|
||||
Manage test baselines for regression testing.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed test baseline --create --name <baseline>
|
||||
z3ed test baseline --compare --name <baseline> [--threshold <percent>]
|
||||
z3ed test baseline --update --name <baseline>
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Create baseline
|
||||
z3ed test baseline --create --name "v1.0_stable"
|
||||
|
||||
# Compare against baseline
|
||||
z3ed test baseline --compare --name "v1.0_stable" --threshold 95
|
||||
|
||||
# Update baseline
|
||||
z3ed test baseline --update --name "v1.0_stable"
|
||||
```
|
||||
|
||||
## Build & CI/CD
|
||||
|
||||
### `z3ed build`
|
||||
Build the project with specified configuration.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed build --preset <preset> [--verbose] [--parallel <jobs>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Debug build
|
||||
z3ed build --preset lin-dbg
|
||||
|
||||
# Release build with 8 parallel jobs
|
||||
z3ed build --preset lin-rel --parallel 8
|
||||
|
||||
# Verbose output
|
||||
z3ed build --preset mac-dbg --verbose
|
||||
```
|
||||
|
||||
### `z3ed ci status`
|
||||
Check CI/CD pipeline status.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed ci status [--workflow <name>] [--branch <branch>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Check all workflows
|
||||
z3ed ci status
|
||||
|
||||
# Check specific workflow
|
||||
z3ed ci status --workflow "Build and Test"
|
||||
|
||||
# Check branch status
|
||||
z3ed ci status --branch develop
|
||||
```
|
||||
|
||||
## Query & Discovery
|
||||
|
||||
### `z3ed query rom-info`
|
||||
Get comprehensive ROM information.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed query rom-info [--detailed] [--format <json|yaml|text>]
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```json
|
||||
{
|
||||
"title": "THE LEGEND OF ZELDA",
|
||||
"size": 2097152,
|
||||
"expanded": true,
|
||||
"version": "1.0",
|
||||
"region": "USA",
|
||||
"checksum": "0xABCD",
|
||||
"header": {
|
||||
"mapper": "LoROM",
|
||||
"rom_speed": "FastROM",
|
||||
"rom_type": "ROM+SRAM+Battery"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `z3ed query available-commands`
|
||||
Discover available commands and their usage.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed query available-commands [--category <category>] [--format tree|list|json]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# List all commands as tree
|
||||
z3ed query available-commands --format tree
|
||||
|
||||
# List editor commands
|
||||
z3ed query available-commands --category editor
|
||||
|
||||
# JSON output for programmatic use
|
||||
z3ed query available-commands --format json
|
||||
```
|
||||
|
||||
### `z3ed query find-tiles`
|
||||
Search for tile patterns in ROM.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed query find-tiles --pattern <hex> [--context <bytes>] [--limit <count>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Find specific tile pattern
|
||||
z3ed query find-tiles --pattern "FF 00 FF 00"
|
||||
|
||||
# Find with context
|
||||
z3ed query find-tiles --pattern "A9 00" --context 8
|
||||
|
||||
# Limit results
|
||||
z3ed query find-tiles --pattern "00 00" --limit 10
|
||||
```
|
||||
|
||||
## Interactive REPL
|
||||
|
||||
### Starting REPL Mode
|
||||
|
||||
```bash
|
||||
# Start REPL with ROM
|
||||
z3ed repl --rom zelda3.sfc
|
||||
|
||||
# Start with history file
|
||||
z3ed repl --rom zelda3.sfc --history ~/.z3ed_history
|
||||
```
|
||||
|
||||
### REPL Features
|
||||
|
||||
#### Variable Assignment
|
||||
```
|
||||
z3ed> $info = rom info
|
||||
z3ed> echo $info.title
|
||||
THE LEGEND OF ZELDA
|
||||
```
|
||||
|
||||
#### Command Pipelines
|
||||
```
|
||||
z3ed> rom read --address 0x1000 --length 100 | find --pattern "A9"
|
||||
z3ed> query find-tiles --pattern "FF" | head -10
|
||||
```
|
||||
|
||||
#### Session Management
|
||||
```
|
||||
z3ed> session save my_session
|
||||
z3ed> session load my_session
|
||||
z3ed> history show
|
||||
z3ed> history replay 5-10
|
||||
```
|
||||
|
||||
## Network & Collaboration
|
||||
|
||||
### `z3ed network connect`
|
||||
Connect to collaborative editing session.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed network connect --host <host> --port <port> [--username <name>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Connect to session
|
||||
z3ed network connect --host localhost --port 8080 --username "agent1"
|
||||
|
||||
# Join specific room
|
||||
z3ed network join --room "dungeon_editing" --password "secret"
|
||||
```
|
||||
|
||||
### `z3ed network sync`
|
||||
Synchronize changes with other collaborators.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed network sync [--push] [--pull] [--merge-strategy <strategy>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Push local changes
|
||||
z3ed network sync --push
|
||||
|
||||
# Pull remote changes
|
||||
z3ed network sync --pull
|
||||
|
||||
# Bidirectional sync with merge
|
||||
z3ed network sync --merge-strategy last-write-wins
|
||||
```
|
||||
|
||||
## AI Integration
|
||||
|
||||
### `z3ed ai chat`
|
||||
Interactive AI assistant for ROM hacking.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed ai chat [--model <model>] [--context <file>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Start chat with default model
|
||||
z3ed ai chat
|
||||
|
||||
# Use specific model
|
||||
z3ed ai chat --model gemini-pro
|
||||
|
||||
# Provide context file
|
||||
z3ed ai chat --context room_layout.json
|
||||
```
|
||||
|
||||
### `z3ed ai suggest`
|
||||
Get AI suggestions for ROM modifications.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed ai suggest --task <task> [--model <model>] [--constraints <json>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Suggest dungeon layout improvements
|
||||
z3ed ai suggest --task "improve dungeon flow" --constraints '{"rooms": [1,2,3]}'
|
||||
|
||||
# Suggest sprite placements
|
||||
z3ed ai suggest --task "balance enemy placement" --model ollama:llama2
|
||||
```
|
||||
|
||||
### `z3ed ai analyze`
|
||||
Analyze ROM for patterns and issues.
|
||||
|
||||
**Syntax:**
|
||||
```bash
|
||||
z3ed ai analyze --type <pattern|bug|optimization> [--report <file>]
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
# Analyze for bugs
|
||||
z3ed ai analyze --type bug --report bugs.json
|
||||
|
||||
# Find optimization opportunities
|
||||
z3ed ai analyze --type optimization
|
||||
|
||||
# Pattern analysis
|
||||
z3ed ai analyze --type pattern --report patterns.md
|
||||
```
|
||||
|
||||
## Global Options
|
||||
|
||||
These options work with all commands:
|
||||
|
||||
- `--rom <file>` - Specify ROM file to use
|
||||
- `--verbose` - Enable verbose output
|
||||
- `--quiet` - Suppress non-error output
|
||||
- `--format <json|yaml|text>` - Output format
|
||||
- `--output <file>` - Write output to file
|
||||
- `--no-color` - Disable colored output
|
||||
- `--config <file>` - Use configuration file
|
||||
- `--log-level <level>` - Set logging level
|
||||
- `--help` - Show help for command
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0` - Success
|
||||
- `1` - General error
|
||||
- `2` - Invalid arguments
|
||||
- `3` - ROM not loaded
|
||||
- `4` - Operation failed
|
||||
- `5` - Network error
|
||||
- `6` - Build error
|
||||
- `7` - Test failure
|
||||
- `127` - Command not found
|
||||
|
||||
## Configuration
|
||||
|
||||
Z3ed can be configured via `~/.z3edrc` or `z3ed.config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"default_rom": "~/roms/zelda3.sfc",
|
||||
"ai_model": "gemini-pro",
|
||||
"output_format": "json",
|
||||
"log_level": "info",
|
||||
"network": {
|
||||
"default_host": "localhost",
|
||||
"default_port": 8080
|
||||
},
|
||||
"build": {
|
||||
"default_preset": "lin-dbg",
|
||||
"parallel_jobs": 8
|
||||
},
|
||||
"test": {
|
||||
"framework": "gtest",
|
||||
"coverage": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `Z3ED_ROM_PATH` - Default ROM file path
|
||||
- `Z3ED_CONFIG` - Configuration file location
|
||||
- `Z3ED_AI_MODEL` - Default AI model
|
||||
- `Z3ED_LOG_LEVEL` - Logging verbosity
|
||||
- `Z3ED_HISTORY_FILE` - REPL history file location
|
||||
- `Z3ED_CACHE_DIR` - Cache directory for snapshots
|
||||
- `Z3ED_API_KEY` - API key for AI services
|
||||
Reference in New Issue
Block a user