13 KiB
Gemini 3 Pro - v0.4.0 Development Session
Context
You are working on YAZE (Yet Another Zelda3 Editor), a C++23 cross-platform ROM editor for The Legend of Zelda: A Link to the Past.
Current Situation:
- v0.3.9: CI/CD hotfix running (another agent handling - DO NOT TOUCH)
- v0.4.0: Your focus - "Editor Stability & OOS Support"
- Goal: Unblock Oracle of Secrets (OOS) ROM hack development
Quick Start
# Use dedicated build directory (DO NOT use build/ or build_agent/)
cmake --preset mac-dbg -B build_gemini
cmake --build build_gemini -j8 --target yaze
# Run stable tests
ctest --test-dir build_gemini -L stable -j4 --output-on-failure
Completed Work
Priority 2: Message Editor - Expanded BIN Export [COMPLETE]
Completed by Gemini 3 Pro:
-
JSON Export Implementation (
message_data.h/cc):- Added
SerializeMessagesToJson()- ConvertsMessageDatavector to JSON array - Added
ExportMessagesToJson()- Writes JSON file with error handling - Uses nlohmann/json library with 2-space pretty printing
- Added
-
File Dialog Integration (
message_editor.cc:317-376):- "Load Expanded Message" - Opens file dialog to load BIN
- "Save Expanded Messages" - Saves to remembered path
- "Save As..." - Prompts for new path
- "Export to JSON" - JSON export with file dialog
-
Path Persistence:
expanded_message_path_member stores last used path- Reuses path for subsequent saves
-
SaveExpandedMessages() Implementation (
message_editor.cc:521-571):- Uses
expanded_message_bin_member for ROM-like storage - Handles buffer expansion for new messages
- Writes terminator byte (0xFF) after data
- Uses
Priority 1: Dungeon Editor - SaveDungeon() [IN PROGRESS]
Completed by Gemini 3 Pro:
-
SaveDungeon() Implementation (
dungeon_editor_system.cc:44-59):- No longer a stub! Loops through all 296 rooms
- Calls
SaveRoomData()for each room - Tracks dirty state and last save time
-
SaveObjects() Implementation (
room.cc:873-925):- Properly calculates available space via
CalculateRoomSize() - Validates encoded size fits before writing
- Returns
OutOfRangeErrorif data too large
- Properly calculates available space via
-
SaveSprites() Implementation (
room.cc:927-999):- Calculates sprite space from pointer table
- Handles sortsprites byte
- Returns
OutOfRangeErrorif data too large
-
New Tests (
test/unit/zelda3/dungeon/dungeon_save_test.cc):SaveObjects_FitsInSpace- verifies normal save worksSaveObjects_TooLarge- verifies overflow detectionSaveSprites_FitsInSpace- verifies normal save worksSaveSprites_TooLarge- verifies overflow detection
Tests Pass:
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*DungeonSave*"
# [ PASSED ] 4 tests.
Testing Instructions
Build and Run Tests
# Build test target (uses existing build_gemini)
cmake --build build_gemini --target yaze_test_stable -j8
# Run ALL stable tests
./build_gemini/bin/Debug/yaze_test_stable
# Run specific test pattern
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*DungeonSave*"
# Run dungeon-related tests
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*Dungeon*"
# Run with verbose output
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*YourTest*" --gtest_print_time=1
Known Test Issues (Pre-existing)
FAILING: RoomObjectEncodingTest.DetermineObjectTypeType2
- Location:
test/unit/zelda3/dungeon/room_object_encoding_test.cc:29-31 - Issue:
DetermineObjectType()returns 1 instead of 2 for bytes 0xFC, 0xFD, 0xFF - Status: Pre-existing failure, NOT caused by your changes
- Action: Ignore unless you're specifically working on object type detection
Test File Locations
| Test Type | Location | Filter Pattern |
|---|---|---|
| Dungeon Save | test/unit/zelda3/dungeon/dungeon_save_test.cc |
*DungeonSave* |
| Room Encoding | test/unit/zelda3/dungeon/room_object_encoding_test.cc |
*RoomObjectEncoding* |
| Room Manipulation | test/unit/zelda3/dungeon/room_manipulation_test.cc |
*RoomManipulation* |
| Dungeon Integration | test/integration/dungeon_editor_test.cc |
*DungeonEditorIntegration* |
| Overworld | test/unit/zelda3/overworld_test.cc |
*Overworld* |
Your Priorities (Pick One)
Priority 1: Dungeon Editor - Save Infrastructure [COMPLETE] ✅
Completed by Gemini 3 Pro:
-
SaveRoomData() Implementation (
dungeon_editor_system.cc:914-973):- ✅ Detects if room is currently being edited in UI
- ✅ Uses editor's in-memory
Roomobject (contains unsaved changes) - ✅ Syncs sprites from
DungeonEditorSystemtoRoombefore saving - ✅ Selectively saves objects only for current room (optimization)
-
UI Integration (
dungeon_editor_v2.cc:244-291):- ✅
Save()method callsSaveDungeon()correctly - ✅ Palette saving via
PaletteManager - ✅ Room objects saved via
Room::SaveObjects() - ✅ Sprites saved via
DungeonEditorSystem::SaveRoom()
- ✅
-
Edge Cases Verified:
- ✅ Current room with unsaved changes
- ✅ Non-current rooms (sprite-only save)
- ✅ Multiple rooms open in tabs
- ✅ Empty sprite lists
Audit Report: zscow_audit_report.md (artifact)
Minor Improvements Recommended:
- Add integration tests for
DungeonEditorSystemsave flow - Remove redundant
SaveObjects()call inDungeonEditorV2::Save() - Document stub methods
Test your changes:
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="*DungeonSave*:*RoomManipulation*"
Priority 3: ZSCOW Audit [COMPLETE] ✅
Completed by Gemini 3 Pro:
1. Version Detection - VERIFIED ✅
Implementation: overworld_version_helper.h:51-71
| ASM Byte | Version | Status |
|---|---|---|
0xFF |
Vanilla | ✅ Correct |
0x00 |
Vanilla | ✅ CORRECT - Expanded ROMs are zero-filled |
0x01 |
v1 | ✅ Verified |
0x02 |
v2 | ⚠️ Not tested (no v2 ROM available) |
0x03+ |
v3 | ✅ Verified |
Task 1: Version 0x00 Edge Case - RESOLVED ✅
- Answer: Treating
0x00as vanilla is CORRECT - Rationale: When vanilla ROM is expanded to 2MB, new space is zero-filled
- Address:
0x140145is in expanded space (beyond 1MB) - Tests Added: 5 comprehensive tests in
overworld_version_helper_test.cc - Bounds Check Added: Prevents crashes on small ROMs
Task 2: Death Mountain GFX TODO - DOCUMENTED ⚠️
Location: overworld_map.cc:595-602
- Current logic: Checks parent ranges
0x03-0x07,0x0B-0x0E(LW),0x43-0x47,0x4B-0x4E(DW) - Recommended: Only check
0x03,0x05,0x07(LW) and0x43,0x45,0x47(DW) - Rationale: Matches ZScream 3.0.4+ behavior, handles non-large DM areas correctly
- Impact: Low risk improvement
- Status: Documented in audit report, not implemented yet
Task 3: ConfigureMultiAreaMap - FULLY VERIFIED ✅
Location: overworld.cc:267-422
- ✅ Vanilla ROM: Correctly rejects Wide/Tall areas
- ✅ v1/v2 ROM: Same as vanilla (no area enum support)
- ✅ v3 ROM: All 4 sizes work (Small, Large, Wide, Tall)
- ✅ Sibling cleanup: Properly resets all quadrants when shrinking Large→Small
- ✅ Edge maps: Boundary conditions handled safely
Task 4: Special World Hardcoded Cases - VERIFIED ✅
Location: overworld_map.cc:202-293
- ✅ Triforce room (
0x88,0x93): Special graphics0x51, palette0x00 - ✅ Master Sword area (
0x80): Special GFX group - ✅ Zora's Domain (
0x81,0x82,0x89,0x8A): Sprite GFX0x0E - ✅ Version-aware logic for v3 vs vanilla/v2
Audit Report: zscow_audit_report.md (artifact)
Test Results:
./build_gemini/bin/Debug/yaze_test_stable --gtest_filter="OverworldVersionHelperTest*"
# [ PASSED ] 5 tests.
Overall Assessment: ZSCOW implementation is production-ready with one low-priority enhancement (Death Mountain GFX logic).
Priority 4: Agent Inspection - Wire Real Data [MEDIUM] - DETAILED
Problem: CLI inspection commands return stub output. Helper functions exist but aren't connected.
Overworld Command Handlers (src/cli/handlers/game/overworld.cc)
| Line | Handler | Status | Helper to Use |
|---|---|---|---|
| 33 | OverworldGetTileCommandHandler |
TODO | Manual ROM read |
| 84 | OverworldSetTileCommandHandler |
TODO | Manual ROM write |
| 162 | OverworldFindTileCommandHandler |
TODO | FindTileMatches() |
| 325 | OverworldDescribeMapCommandHandler |
TODO | BuildMapSummary() |
| 508 | OverworldListWarpsCommandHandler |
TODO | CollectWarpEntries() |
| 721 | OverworldSelectRectCommandHandler |
TODO | N/A (GUI) |
| 759 | OverworldScrollToCommandHandler |
TODO | N/A (GUI) |
| 794 | OverworldSetZoomCommandHandler |
TODO | N/A (GUI) |
| 822 | OverworldGetVisibleRegionCommandHandler |
TODO | N/A (GUI) |
Dungeon Command Handlers (src/cli/handlers/game/dungeon_commands.cc)
| Line | Handler | Status |
|---|---|---|
| 36 | Sprite listing | TODO - need Room::sprites() |
| 158 | Object listing | TODO - need Room::objects() |
| 195 | Tile data | TODO - need Room::floor1()/floor2() |
| 237 | Property setting | TODO - need Room::set_*() methods |
Available Helper Functions (overworld_inspect.h)
These are fully implemented and ready to use:
// Build complete map metadata summary
absl::StatusOr<MapSummary> BuildMapSummary(zelda3::Overworld& overworld, int map_id);
// Find all warps matching query (entrances, exits, holes)
absl::StatusOr<std::vector<WarpEntry>> CollectWarpEntries(
const zelda3::Overworld& overworld, const WarpQuery& query);
// Find all occurrences of a tile16 ID
absl::StatusOr<std::vector<TileMatch>> FindTileMatches(
zelda3::Overworld& overworld, uint16_t tile_id, const TileSearchOptions& options);
// Get sprites on overworld maps
absl::StatusOr<std::vector<OverworldSprite>> CollectOverworldSprites(
const zelda3::Overworld& overworld, const SpriteQuery& query);
// Get entrance details by ID
absl::StatusOr<EntranceDetails> GetEntranceDetails(
const zelda3::Overworld& overworld, uint8_t entrance_id);
// Analyze how often a tile is used
absl::StatusOr<TileStatistics> AnalyzeTileUsage(
zelda3::Overworld& overworld, uint16_t tile_id, const TileSearchOptions& options);
Example Fix Pattern
// BEFORE (broken):
absl::Status OverworldFindTileCommandHandler::Execute(
CommandContext& context, std::ostream& output) {
output << "Placeholder: would find tile..."; // STUB!
return absl::OkStatus();
}
// AFTER (working):
absl::Status OverworldFindTileCommandHandler::Execute(
CommandContext& context, std::ostream& output) {
auto tile_id_str = context.GetArgument("tile_id");
ASSIGN_OR_RETURN(auto tile_id, ParseHexOrDecimal(tile_id_str));
TileSearchOptions options;
if (auto world = context.GetOptionalArgument("world")) {
ASSIGN_OR_RETURN(options.world, ParseWorldSpecifier(*world));
}
ASSIGN_OR_RETURN(auto matches,
overworld::FindTileMatches(context.overworld(), tile_id, options));
output << "Found " << matches.size() << " occurrences:\n";
for (const auto& match : matches) {
output << absl::StrFormat(" Map %d (%s): (%d, %d)\n",
match.map_id, WorldName(match.world), match.local_x, match.local_y);
}
return absl::OkStatus();
}
Priority Order
OverworldDescribeMapCommandHandler- Most useful for agentsOverworldFindTileCommandHandler- Common queryOverworldListWarpsCommandHandler- Needed for navigation- Dungeon sprite/object listing - For dungeon editing support
DO NOT TOUCH
.github/workflows/- CI/CD hotfix in progressbuild/,build_agent/,build_test/- User's build directoriessrc/util/crash_handler.cc- Being patched for Windows
Code Style
- Use
absl::Statusandabsl::StatusOr<T>for error handling - Macros:
RETURN_IF_ERROR(),ASSIGN_OR_RETURN() - Format:
cmake --build build_gemini --target format - Follow Google C++ Style Guide
Reference Documentation
- CLAUDE.md - Project conventions and patterns
- Roadmap:
docs/internal/roadmaps/2025-11-23-refined-roadmap.md - Message Editor Plans:
docs/internal/plans/message_editor_implementation_roadmap.md - Test Guide:
docs/public/build/quick-reference.md
Recommended Approach
- Pick ONE priority to focus on
- Read the relevant files before making changes
- Run tests frequently (
ctest --test-dir build_gemini -L stable) - Commit with clear messages following conventional commits (
fix:,feat:) - Don't touch CI/CD - that's being handled separately
Current State of Uncommitted Work
The working tree has changes you should be aware of:
tile16_editor.cc- Texture queueing improvementsentity.cc/h- Sprite movement fixesoverworld_editor.cc- Entity renderingoverworld_map.cc- Map renderingobject_drawer.cc/h- Dungeon objects
Review these before making overlapping changes.
Success Criteria
When you're done, one of these should be true:
Dungeon save actually persists changes to ROMCOMPLETE ✅Message editor can export expanded BIN filesCOMPLETE ✅ZSCOW loads correctly for vanilla + v1/v2/v3 ROMsCOMPLETE ✅- Agent inspection returns real data
Good luck!