feat: Introduce Debugging and Testing Guide with enhanced startup options
- Added a new document, E5-debugging-guide.md, providing comprehensive strategies for debugging and testing the `yaze` application, including logging practices and testing frameworks. - Updated E2-development-guide.md to include a new section on debugging and testing, detailing quick debugging methods using command-line flags for specific editors and UI cards. - Enhanced the main application to support command-line flags for opening specific editors and cards on startup, improving the developer experience. - Refactored the Controller class to handle startup editor and card initialization based on command-line inputs.
This commit is contained in:
@@ -104,7 +104,37 @@ To ensure a consistent and polished look and feel, all new UI components must ad
|
||||
- **Items**: Bright red
|
||||
- **Sprites**: Bright magenta
|
||||
|
||||
### 3.5. Bitmap and Texture Synchronization
|
||||
## 4. Debugging and Testing
|
||||
|
||||
### 4.1. Quick Debugging with Startup Flags
|
||||
|
||||
To accelerate your debugging workflow, use command-line flags to jump directly to specific editors and open relevant UI cards:
|
||||
|
||||
```bash
|
||||
# Quick dungeon room testing
|
||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
|
||||
|
||||
# Compare multiple rooms
|
||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
|
||||
|
||||
# Full dungeon workspace
|
||||
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
|
||||
--cards="Rooms List,Room Matrix,Object Editor,Palette Editor"
|
||||
|
||||
# Enable debug logging
|
||||
./yaze --debug --log_file=debug.log --rom_file=zelda3.sfc --editor=Dungeon
|
||||
```
|
||||
|
||||
**Available Editors**: Assembly, Dungeon, Graphics, Music, Overworld, Palette, Screen, Sprite, Message, Hex, Agent, Settings
|
||||
|
||||
**Dungeon Editor Cards**: Rooms List, Room Matrix, Entrances List, Room Graphics, Object Editor, Palette Editor, Room N (where N is room ID 0-319)
|
||||
|
||||
See [debugging-startup-flags.md](debugging-startup-flags.md) for complete documentation.
|
||||
|
||||
### 4.2. Testing Strategies
|
||||
|
||||
For a comprehensive overview of debugging tools and testing strategies, including how to use the logging framework, command-line test runners, and the GUI automation harness for AI agents, please refer to the [Debugging and Testing Guide](E5-debugging-guide.md).
|
||||
|
||||
|
||||
When working with bitmaps and textures, understand that two memory locations must stay synchronized:
|
||||
|
||||
|
||||
224
docs/E5-debugging-guide.md
Normal file
224
docs/E5-debugging-guide.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# E5 - Debugging and Testing Guide
|
||||
|
||||
**Last Updated**: October 9, 2025
|
||||
**Status**: Active
|
||||
|
||||
This document provides a comprehensive guide to debugging and testing the `yaze` application. It covers strategies for developers and provides the necessary information for AI agents to interact with, test, and validate the application.
|
||||
|
||||
---
|
||||
|
||||
## 1. Standardized Logging for Print Debugging
|
||||
|
||||
For all print-based debugging, `yaze` uses a structured logging system defined in `util/log.h`. This is the **only** approved method for logging; direct use of `printf` or `std::cout` should be avoided and replaced with the appropriate `LOG_*` macro.
|
||||
|
||||
### Log Levels and Usage
|
||||
|
||||
- `LOG_DEBUG(category, "message", ...)`: For verbose, development-only information.
|
||||
- `LOG_INFO(category, "message", ...)`: For general, informational messages.
|
||||
- `LOG_WARN(category, "message", ...)`: For potential issues that don't break functionality.
|
||||
- `LOG_ERROR(category, "message", ...)`: For errors that cause a specific operation to fail.
|
||||
|
||||
### Log Categories
|
||||
|
||||
Categories allow you to filter logs to focus on a specific subsystem. Common categories include:
|
||||
- `"Main"`
|
||||
- `"TestManager"`
|
||||
- `"EditorManager"`
|
||||
- `"APU"`, `"CPU"`, `"SNES"` (for the emulator)
|
||||
|
||||
### Enabling and Configuring Logs via CLI
|
||||
|
||||
You can control logging behavior using command-line flags when launching `yaze` or `yaze_test`.
|
||||
|
||||
- **Enable Verbose Debug Logging**:
|
||||
```bash
|
||||
./build/bin/yaze --debug
|
||||
```
|
||||
|
||||
- **Log to a File**:
|
||||
```bash
|
||||
./build/bin/yaze --log_file=yaze_debug.log
|
||||
```
|
||||
|
||||
- **Filter by Category**:
|
||||
```bash
|
||||
# Only show logs from the APU and CPU emulator components
|
||||
./build/bin/yaze_emu --emu_debug_apu=true --emu_debug_cpu=true
|
||||
```
|
||||
|
||||
**Best Practice**: When debugging a specific component, add detailed `LOG_DEBUG` statements with a unique category. Then, run `yaze` with the appropriate flags to isolate the output.
|
||||
|
||||
---
|
||||
|
||||
## 2. Command-Line Workflows for Testing
|
||||
|
||||
The `yaze` ecosystem provides several executables and flags to streamline testing and debugging.
|
||||
|
||||
### Launching the GUI for Specific Tasks
|
||||
|
||||
- **Load a ROM on Startup**: To immediately test a specific ROM, use the `--rom_file` flag. This bypasses the welcome screen.
|
||||
```bash
|
||||
./build/bin/yaze --rom_file /path/to/your/zelda3.sfc
|
||||
```
|
||||
|
||||
- **Enable the GUI Test Harness**: To allow the `z3ed` CLI to automate the GUI, you must start `yaze` with the gRPC server enabled.
|
||||
```bash
|
||||
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness
|
||||
```
|
||||
|
||||
- **Open a Specific Editor and Cards**: To quickly test a specific editor and its components, use the `--editor` and `--cards` flags. This is especially useful for debugging complex UIs like the Dungeon Editor.
|
||||
```bash
|
||||
# Open the Dungeon Editor with the Room Matrix and two specific room cards
|
||||
./build/bin/yaze --rom_file zelda3.sfc --editor=Dungeon --cards="Room Matrix,Room 0,Room 105"
|
||||
|
||||
# Available editors: Assembly, Dungeon, Graphics, Music, Overworld, Palette,
|
||||
# Screen, Sprite, Message, Hex, Agent, Settings
|
||||
|
||||
# Dungeon editor cards: Rooms List, Room Matrix, Entrances List, Room Graphics,
|
||||
# Object Editor, Palette Editor, Room N (where N is room ID)
|
||||
```
|
||||
|
||||
**Quick Examples**:
|
||||
```bash
|
||||
# Fast dungeon room testing
|
||||
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
|
||||
|
||||
# Compare multiple rooms side-by-side
|
||||
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
|
||||
|
||||
# Full dungeon workspace with all tools
|
||||
./build/bin/yaze --rom_file=zelda3.sfc --editor=Dungeon \
|
||||
--cards="Rooms List,Room Matrix,Object Editor,Palette Editor"
|
||||
|
||||
# Jump straight to overworld editing
|
||||
./build/bin/yaze --rom_file=zelda3.sfc --editor=Overworld
|
||||
```
|
||||
|
||||
For a complete reference, see [docs/debugging-startup-flags.md](debugging-startup-flags.md).
|
||||
|
||||
### Running Automated C++ Tests
|
||||
|
||||
The `yaze_test` executable is used to run the project's suite of unit, integration, and E2E tests.
|
||||
|
||||
- **Run All Tests**:
|
||||
```bash
|
||||
./build_ai/bin/yaze_test
|
||||
```
|
||||
|
||||
- **Run Specific Categories**:
|
||||
```bash
|
||||
# Run only fast, dependency-free unit tests
|
||||
./build_ai/bin/yaze_test --unit
|
||||
|
||||
# Run tests that require a ROM file
|
||||
./build_ai/bin/yaze_test --rom-dependent --rom-path /path/to/zelda3.sfc
|
||||
```
|
||||
|
||||
- **Run GUI-based E2E Tests**:
|
||||
```bash
|
||||
# Run E2E tests and watch the GUI interactions
|
||||
./build_ai/bin/yaze_test --e2e --show-gui
|
||||
```
|
||||
|
||||
### Inspecting ROMs with `z3ed`
|
||||
|
||||
The `z3ed` CLI is a powerful tool for inspecting ROM data without launching the full GUI. This is ideal for quick checks and scripting.
|
||||
|
||||
- **Get ROM Information**:
|
||||
```bash
|
||||
z3ed rom info --rom zelda3.sfc
|
||||
```
|
||||
|
||||
- **Inspect Dungeon Sprites**:
|
||||
```bash
|
||||
z3ed dungeon list-sprites --rom zelda3.sfc --dungeon 2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. GUI Automation for AI Agents
|
||||
|
||||
The primary way for an AI agent to test its changes and interact with `yaze` is through the GUI automation framework. This system consists of the `yaze` gRPC server (Test Harness) and the `z3ed` CLI client.
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
1. **`yaze` (Server)**: When launched with `--enable_test_harness`, it starts a gRPC server that exposes the UI for automation.
|
||||
2. **`z3ed` (Client)**: The `z3ed agent test` commands connect to the gRPC server to send commands and receive information.
|
||||
3. **AI Agent**: The agent generates `z3ed` commands to drive the UI and verify its actions.
|
||||
|
||||
### Step-by-Step Workflow for AI
|
||||
|
||||
#### Step 1: Launch `yaze` with the Test Harness
|
||||
|
||||
The AI must first ensure the `yaze` GUI is running and ready for automation.
|
||||
|
||||
```bash
|
||||
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness --test_harness_port 50051
|
||||
```
|
||||
|
||||
#### Step 2: Discover UI Elements
|
||||
|
||||
Before interacting with the UI, the agent needs to know the stable IDs of the widgets.
|
||||
|
||||
```bash
|
||||
# Discover all widgets in the Dungeon editor window
|
||||
z3ed agent test discover --window "Dungeon" --grpc localhost:50051
|
||||
```
|
||||
This will return a list of widget IDs (e.g., `Dungeon/Canvas/Map`) that can be used in scripts.
|
||||
|
||||
**Tip**: You can also launch `yaze` with the `--editor` flag to automatically open a specific editor:
|
||||
```bash
|
||||
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness --editor=Dungeon --cards="Room 0"
|
||||
```
|
||||
|
||||
#### Step 3: Record or Write a Test Script
|
||||
|
||||
An agent can either generate a test script from scratch or use a pre-recorded one.
|
||||
|
||||
- **Recording a human interaction**:
|
||||
```bash
|
||||
z3ed agent test record --suite my_test.jsonl
|
||||
```
|
||||
- **A generated script might look like this**:
|
||||
```json
|
||||
// my_test.jsonl
|
||||
{"action": "click", "target": "Dungeon/Toolbar/Open Room"}
|
||||
{"action": "wait", "duration_ms": 500}
|
||||
{"action": "type", "target": "Room Selector/Filter", "text": "Room 105"}
|
||||
{"action": "click", "target": "Room Selector/List/Room 105"}
|
||||
{"action": "assert_visible", "target": "Room Card 105"}
|
||||
```
|
||||
|
||||
- **Or use startup flags to prepare the environment**:
|
||||
```bash
|
||||
# Start yaze with the room already open
|
||||
./build/bin/yaze --rom_file zelda3.sfc --enable_test_harness \
|
||||
--editor=Dungeon --cards="Room 105"
|
||||
|
||||
# Then your test script just needs to validate the state
|
||||
{"action": "assert_visible", "target": "Room Card 105"}
|
||||
{"action": "assert_visible", "target": "Dungeon/Canvas"}
|
||||
```
|
||||
|
||||
#### Step 4: Replay the Test and Verify
|
||||
|
||||
The agent executes the script to perform the actions and validate the outcome.
|
||||
|
||||
```bash
|
||||
z3ed agent test replay my_test.jsonl --watch
|
||||
```
|
||||
The `--watch` flag streams results back to the CLI in real-time. The agent can parse this output to confirm its actions were successful.
|
||||
|
||||
---
|
||||
|
||||
## 4. Advanced Debugging Tools
|
||||
|
||||
For more complex issues, especially within the emulator, `yaze` provides several advanced debugging windows. These are covered in detail in the [Emulator Development Guide](E4-Emulator-Development-Guide.md).
|
||||
|
||||
- **Disassembly Viewer**: A live, interactive view of the 65816 and SPC700 CPU execution.
|
||||
- **Breakpoint Manager**: Set breakpoints on code execution, memory reads, or memory writes.
|
||||
- **Memory Viewer**: Inspect WRAM, SRAM, VRAM, and ROM.
|
||||
- **APU Inspector**: A dedicated debugger for the audio subsystem.
|
||||
- **Event Viewer**: A timeline of all hardware events (NMI, IRQ, DMA).
|
||||
|
||||
These tools are accessible from the **Debug** menu in the main application.
|
||||
@@ -20,7 +20,8 @@ Welcome to the official documentation for yaze, a comprehensive ROM editor for T
|
||||
- [E1: Assembly Style Guide](E1-asm-style-guide.md) - 65816 assembly coding standards.
|
||||
- [E2: Development Guide](E2-development-guide.md) - Core architectural patterns, UI systems, and best practices.
|
||||
- [E3: API Reference](E3-api-reference.md) - C/C++ API documentation for extensions.
|
||||
- [E4: Emulator Development Guide](E4-Emulator-Development-Guide.md) - A master guide to the SNES emulator subsystem.
|
||||
- [E4: Emulator Development Guide](E4-Emulator-Development-Guide.md)
|
||||
- [E5: Debugging and Testing Guide](E5-debugging-guide.md) - A master guide to the SNES emulator subsystem.
|
||||
|
||||
## F: Technical Documentation
|
||||
- [F1: Dungeon Editor Guide](F1-dungeon-editor-guide.md) - A master guide to the dungeon editing system.
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/timing.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/editor/editor_manager.h"
|
||||
#include "app/editor/ui/background_renderer.h"
|
||||
#include "app/gfx/arena.h" // Add include for Arena
|
||||
#include "app/gfx/backend/sdl2_renderer.h" // Add include for new renderer
|
||||
#include "app/gfx/arena.h" // Add include for Arena
|
||||
#include "app/gfx/backend/sdl2_renderer.h" // Add include for new renderer
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/gui/widgets/widget_id_registry.h"
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
@@ -21,23 +23,33 @@ namespace core {
|
||||
absl::Status Controller::OnEntry(std::string filename) {
|
||||
// Create renderer FIRST
|
||||
renderer_ = std::make_unique<gfx::SDL2Renderer>();
|
||||
|
||||
|
||||
// Call CreateWindow with our renderer
|
||||
RETURN_IF_ERROR(CreateWindow(window_, renderer_.get(), SDL_WINDOW_RESIZABLE));
|
||||
|
||||
|
||||
// Initialize the graphics Arena with the renderer
|
||||
gfx::Arena::Get().Initialize(renderer_.get());
|
||||
|
||||
// Set up audio for emulator
|
||||
editor_manager_.emulator().set_audio_buffer(window_.audio_buffer_.get());
|
||||
editor_manager_.emulator().set_audio_device_id(window_.audio_device_);
|
||||
|
||||
|
||||
// Initialize editor manager with renderer
|
||||
editor_manager_.Initialize(renderer_.get(), filename);
|
||||
|
||||
active_ = true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void Controller::SetStartupEditor(const std::string& editor_name,
|
||||
const std::string& cards) {
|
||||
// Process command-line flags for editor and cards
|
||||
// Example: --editor=Dungeon --cards="Rooms List,Room 0,Room 105"
|
||||
if (!editor_name.empty()) {
|
||||
editor_manager_.OpenEditorAndCardsFromFlags(editor_name, cards);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::OnInput() {
|
||||
PRINT_IF_ERROR(HandleEvents(window_));
|
||||
}
|
||||
@@ -53,7 +65,7 @@ absl::Status Controller::OnLoad() {
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->WorkPos);
|
||||
ImGui::SetNextWindowSize(viewport->WorkSize);
|
||||
ImGui::SetNextWindowViewport(viewport->ID);
|
||||
@@ -73,8 +85,8 @@ absl::Status Controller::OnLoad() {
|
||||
|
||||
// Create DockSpace first
|
||||
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
|
||||
gui::DockSpaceRenderer::BeginEnhancedDockSpace(dockspace_id, ImVec2(0.0f, 0.0f),
|
||||
ImGuiDockNodeFlags_PassthruCentralNode);
|
||||
gui::DockSpaceRenderer::BeginEnhancedDockSpace(
|
||||
dockspace_id, ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_PassthruCentralNode);
|
||||
|
||||
editor_manager_.DrawMenuBar(); // Draw the fixed menu bar at the top
|
||||
|
||||
@@ -93,13 +105,14 @@ void Controller::DoRender() const {
|
||||
|
||||
ImGui::Render();
|
||||
renderer_->Clear();
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(),
|
||||
static_cast<SDL_Renderer*>(renderer_->GetBackendRenderer()));
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(
|
||||
ImGui::GetDrawData(),
|
||||
static_cast<SDL_Renderer*>(renderer_->GetBackendRenderer()));
|
||||
renderer_->Present();
|
||||
|
||||
|
||||
// Use TimingManager for accurate frame timing in sync with SDL
|
||||
float delta_time = TimingManager::Get().Update();
|
||||
|
||||
|
||||
// Gentle frame rate cap to prevent excessive CPU usage
|
||||
// Only delay if we're rendering faster than 144 FPS (< 7ms per frame)
|
||||
if (delta_time < 0.007f) {
|
||||
@@ -107,11 +120,10 @@ void Controller::DoRender() const {
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::OnExit() {
|
||||
void Controller::OnExit() {
|
||||
renderer_->Shutdown();
|
||||
PRINT_IF_ERROR(ShutdownWindow(window_));
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace yaze
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ class Controller {
|
||||
absl::Status OnLoad();
|
||||
void DoRender() const;
|
||||
void OnExit();
|
||||
|
||||
// Set startup editor and cards from command-line flags
|
||||
void SetStartupEditor(const std::string& editor_name, const std::string& cards);
|
||||
|
||||
auto window() -> SDL_Window* { return window_.window_.get(); }
|
||||
void set_active(bool active) { active_ = active; }
|
||||
|
||||
@@ -181,11 +181,6 @@ absl::Status AssemblyEditor::Load() {
|
||||
// Note: Assembly editor uses dynamic file tabs, so we register the main editor window
|
||||
auto& card_manager = gui::EditorCardManager::Get();
|
||||
|
||||
// The assembly editor itself acts as a card when shown
|
||||
// Individual files are tabs within it, not separate cards
|
||||
|
||||
printf("[AssemblyEditor] Assembly editor uses dynamic file tabs\n");
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "app/zelda3/dungeon/room.h"
|
||||
#include "app/zelda3/sprite/sprite.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
@@ -337,30 +338,30 @@ void DungeonCanvasViewer::CalculateWallDimensions(const zelda3::RoomObject& obje
|
||||
|
||||
// Room graphics management methods
|
||||
absl::Status DungeonCanvasViewer::LoadAndRenderRoomGraphics(int room_id) {
|
||||
printf("[LoadAndRender] START room_id=%d\n", room_id);
|
||||
LOG_DEBUG("[LoadAndRender]", "START room_id=%d", room_id);
|
||||
|
||||
if (room_id < 0 || room_id >= 128) {
|
||||
printf("[LoadAndRender] ERROR: Invalid room ID\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "ERROR: Invalid room ID");
|
||||
return absl::InvalidArgumentError("Invalid room ID");
|
||||
}
|
||||
|
||||
if (!rom_ || !rom_->is_loaded()) {
|
||||
printf("[LoadAndRender] ERROR: ROM not loaded\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "ERROR: ROM not loaded");
|
||||
return absl::FailedPreconditionError("ROM not loaded");
|
||||
}
|
||||
|
||||
if (!rooms_) {
|
||||
printf("[LoadAndRender] ERROR: Room data not available\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "ERROR: Room data not available");
|
||||
return absl::FailedPreconditionError("Room data not available");
|
||||
}
|
||||
|
||||
auto& room = (*rooms_)[room_id];
|
||||
printf("[LoadAndRender] Got room reference\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "Got room reference");
|
||||
|
||||
// Load room graphics with proper blockset
|
||||
printf("[LoadAndRender] Loading graphics for blockset %d\n", room.blockset);
|
||||
LOG_DEBUG("[LoadAndRender]", "Loading graphics for blockset %d", room.blockset);
|
||||
room.LoadRoomGraphics(room.blockset);
|
||||
printf("[LoadAndRender] Graphics loaded\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "Graphics loaded");
|
||||
|
||||
// Load the room's palette with bounds checking
|
||||
if (room.palette < rom_->paletteset_ids.size() &&
|
||||
@@ -373,22 +374,22 @@ absl::Status DungeonCanvasViewer::LoadAndRenderRoomGraphics(int room_id) {
|
||||
auto full_palette = rom_->palette_group().dungeon_main[current_palette_group_id_];
|
||||
ASSIGN_OR_RETURN(current_palette_group_,
|
||||
gfx::CreatePaletteGroupFromLargePalette(full_palette));
|
||||
printf("[LoadAndRender] Palette loaded: group_id=%zu\n", current_palette_group_id_);
|
||||
LOG_DEBUG("[LoadAndRender]", "Palette loaded: group_id=%zu", current_palette_group_id_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render the room graphics to the graphics arena
|
||||
printf("[LoadAndRender] Calling room.RenderRoomGraphics()...\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "Calling room.RenderRoomGraphics()...");
|
||||
room.RenderRoomGraphics();
|
||||
printf("[LoadAndRender] RenderRoomGraphics() complete\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "RenderRoomGraphics() complete");
|
||||
|
||||
// Update the background layers with proper palette
|
||||
printf("[LoadAndRender] Updating background layers...\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "Updating background layers...");
|
||||
RETURN_IF_ERROR(UpdateRoomBackgroundLayers(room_id));
|
||||
printf("[LoadAndRender] UpdateRoomBackgroundLayers() complete\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "UpdateRoomBackgroundLayers() complete");
|
||||
|
||||
printf("[LoadAndRender] SUCCESS\n");
|
||||
LOG_DEBUG("[LoadAndRender]", "SUCCESS");
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -475,10 +476,10 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
|
||||
// Only draw if texture was successfully created
|
||||
if (bg1_bitmap.texture()) {
|
||||
printf("[RenderRoomBackgroundLayers] Drawing BG1 bitmap to canvas with texture %p\n", bg1_bitmap.texture());
|
||||
LOG_DEBUG("DungeonCanvasViewer", "Drawing BG1 bitmap to canvas with texture %p", bg1_bitmap.texture());
|
||||
canvas_.DrawBitmap(bg1_bitmap, 0, 0, 1.0f, 255);
|
||||
} else {
|
||||
printf("[RenderRoomBackgroundLayers] ERROR: BG1 bitmap has no texture!\n");
|
||||
LOG_DEBUG("DungeonCanvasViewer", "ERROR: BG1 bitmap has no texture!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,16 +499,16 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
// Use the selected BG2 layer type alpha value
|
||||
const int bg2_alpha_values[] = {255, 191, 127, 64, 0};
|
||||
int alpha_value = bg2_alpha_values[std::min(bg2_layer_type_, 4)];
|
||||
printf("[RenderRoomBackgroundLayers] Drawing BG2 bitmap to canvas with texture %p, alpha=%d\n", bg2_bitmap.texture(), alpha_value);
|
||||
LOG_DEBUG("DungeonCanvasViewer", "Drawing BG2 bitmap to canvas with texture %p, alpha=%d", bg2_bitmap.texture(), alpha_value);
|
||||
canvas_.DrawBitmap(bg2_bitmap, 0, 0, 1.0f, alpha_value);
|
||||
} else {
|
||||
printf("[RenderRoomBackgroundLayers] ERROR: BG2 bitmap has no texture!\n");
|
||||
LOG_DEBUG("DungeonCanvasViewer", "ERROR: BG2 bitmap has no texture!");
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG: Check if background buffers have content
|
||||
if (bg1_bitmap.is_active() && bg1_bitmap.width() > 0) {
|
||||
printf("[RenderRoomBackgroundLayers] BG1 bitmap: %dx%d, active=%d, visible=%d, texture=%p\n",
|
||||
LOG_DEBUG("DungeonCanvasViewer", "BG1 bitmap: %dx%d, active=%d, visible=%d, texture=%p",
|
||||
bg1_bitmap.width(), bg1_bitmap.height(), bg1_bitmap.is_active(), bg1_visible_, bg1_bitmap.texture());
|
||||
|
||||
// Check bitmap data content
|
||||
@@ -516,11 +517,11 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
for (size_t i = 0; i < bg1_data.size(); i += 100) { // Sample every 100th pixel
|
||||
if (bg1_data[i] != 0) non_zero_pixels++;
|
||||
}
|
||||
printf("[RenderRoomBackgroundLayers] BG1 bitmap data: %zu pixels, ~%d non-zero samples\n",
|
||||
LOG_DEBUG("DungeonCanvasViewer", "BG1 bitmap data: %zu pixels, ~%d non-zero samples",
|
||||
bg1_data.size(), non_zero_pixels);
|
||||
}
|
||||
if (bg2_bitmap.is_active() && bg2_bitmap.width() > 0) {
|
||||
printf("[RenderRoomBackgroundLayers] BG2 bitmap: %dx%d, active=%d, visible=%d, layer_type=%d, texture=%p\n",
|
||||
LOG_DEBUG("DungeonCanvasViewer", "BG2 bitmap: %dx%d, active=%d, visible=%d, layer_type=%d, texture=%p",
|
||||
bg2_bitmap.width(), bg2_bitmap.height(), bg2_bitmap.is_active(), bg2_visible_, bg2_layer_type_, bg2_bitmap.texture());
|
||||
|
||||
// Check bitmap data content
|
||||
@@ -529,7 +530,7 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
for (size_t i = 0; i < bg2_data.size(); i += 100) { // Sample every 100th pixel
|
||||
if (bg2_data[i] != 0) non_zero_pixels++;
|
||||
}
|
||||
printf("[RenderRoomBackgroundLayers] BG2 bitmap data: %zu pixels, ~%d non-zero samples\n",
|
||||
LOG_DEBUG("DungeonCanvasViewer", "BG2 bitmap data: %zu pixels, ~%d non-zero samples",
|
||||
bg2_data.size(), non_zero_pixels);
|
||||
}
|
||||
|
||||
@@ -542,9 +543,9 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
IM_COL32(255, 0, 0, 255)); // Bright red
|
||||
|
||||
// DEBUG: Show canvas and bitmap info
|
||||
printf("[RenderRoomBackgroundLayers] Canvas pos: (%.1f, %.1f), Canvas size: (%.1f, %.1f)\n",
|
||||
LOG_DEBUG("DungeonCanvasViewer", "Canvas pos: (%.1f, %.1f), Canvas size: (%.1f, %.1f)",
|
||||
canvas_pos.x, canvas_pos.y, canvas_.canvas_size().x, canvas_.canvas_size().y);
|
||||
printf("[RenderRoomBackgroundLayers] BG1 bitmap size: %dx%d, BG2 bitmap size: %dx%d\n",
|
||||
LOG_DEBUG("DungeonCanvasViewer", "BG1 bitmap size: %dx%d, BG2 bitmap size: %dx%d",
|
||||
bg1_bitmap.width(), bg1_bitmap.height(), bg2_bitmap.width(), bg2_bitmap.height());
|
||||
}
|
||||
|
||||
|
||||
@@ -70,9 +70,6 @@ void DungeonEditor::Initialize() {
|
||||
config.grid_size = 16; // 16x16 tiles
|
||||
object_editor_->SetConfig(config);
|
||||
}
|
||||
|
||||
// Initialize manual renderer for debugging
|
||||
printf("[DungeonEditor] Manual renderer initialized\n");
|
||||
}
|
||||
|
||||
absl::Status DungeonEditor::Load() {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
@@ -97,8 +98,6 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
|
||||
.visibility_flag = &show_palette_editor_,
|
||||
.priority = 70
|
||||
});
|
||||
|
||||
printf("[DungeonEditorV2] Registered 7 cards with EditorCardManager\n");
|
||||
}
|
||||
|
||||
void DungeonEditorV2::Initialize() {}
|
||||
@@ -214,7 +213,7 @@ absl::Status DungeonEditorV2::Save() {
|
||||
auto status = room.SaveObjects();
|
||||
if (!status.ok()) {
|
||||
// Log error but continue with other rooms
|
||||
std::printf("Failed to save room: %s\n", status.message().data());
|
||||
LOG_ERROR("DungeonEditorV2", "Failed to save room: %s", status.message().data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,15 @@ class DungeonEditorV2 : public Editor {
|
||||
return absl::StrFormat("ROM loaded: %s", rom_->title());
|
||||
}
|
||||
|
||||
// Card visibility flags - Public for command-line flag access
|
||||
bool show_room_selector_ = false; // Room selector/list card
|
||||
bool show_room_matrix_ = false; // Dungeon matrix layout
|
||||
bool show_entrances_list_ = false; // Entrance list card (renamed from entrances_matrix_)
|
||||
bool show_room_graphics_ = false; // Room graphics card
|
||||
bool show_object_editor_ = false; // Object editor card
|
||||
bool show_palette_editor_ = false; // Palette editor card
|
||||
bool show_control_panel_ = true; // Control panel (visible by default)
|
||||
|
||||
private:
|
||||
gfx::IRenderer* renderer_ = nullptr;
|
||||
// Simple UI layout
|
||||
@@ -119,16 +128,6 @@ class DungeonEditorV2 : public Editor {
|
||||
std::unordered_map<int, std::shared_ptr<gui::EditorCard>> room_cards_;
|
||||
int current_room_id_ = 0;
|
||||
|
||||
// Card visibility flags - Start with only control panel visible
|
||||
// Other cards hidden by default to prevent crash on ROM load
|
||||
// User can open them via View menu or shortcuts
|
||||
bool show_room_selector_ = false;
|
||||
bool show_room_matrix_ = false;
|
||||
bool show_entrances_list_ = false;
|
||||
bool show_room_graphics_ = false;
|
||||
bool show_object_editor_ = false;
|
||||
bool show_palette_editor_ = false;
|
||||
bool show_control_panel_ = true; // Only control panel visible on start
|
||||
bool control_panel_minimized_ = false;
|
||||
|
||||
// Palette management
|
||||
|
||||
@@ -51,6 +51,7 @@ struct EditorContext {
|
||||
};
|
||||
|
||||
enum class EditorType {
|
||||
kUnknown,
|
||||
kAssembly,
|
||||
kDungeon,
|
||||
kEmulator,
|
||||
@@ -66,7 +67,8 @@ enum class EditorType {
|
||||
kSettings,
|
||||
};
|
||||
|
||||
constexpr std::array<const char*, 13> kEditorNames = {
|
||||
constexpr std::array<const char*, 14> kEditorNames = {
|
||||
"Unknown",
|
||||
"Assembly", "Dungeon", "Emulator", "Graphics", "Music", "Overworld",
|
||||
"Palette", "Screen", "Sprite", "Message", "Hex", "Agent", "Settings",
|
||||
};
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "app/core/features.h"
|
||||
#include "app/core/timing.h"
|
||||
#include "util/file_util.h"
|
||||
#include "app/gui/widgets/widget_id_registry.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "util/log.h"
|
||||
#include "util/platform_paths.h"
|
||||
#include "app/core/project.h"
|
||||
#include "app/editor/code/assembly_editor.h"
|
||||
@@ -81,126 +84,7 @@ std::string GetEditorName(EditorType type) {
|
||||
|
||||
} // namespace
|
||||
|
||||
// Settings + preset helpers
|
||||
void EditorManager::LoadUserSettings() {
|
||||
auto config_dir = util::PlatformPaths::GetConfigDirectory();
|
||||
if (!config_dir.ok()) {
|
||||
LOG_WARN("EditorManager", "Could not determine config directory for settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string settings_path = (*config_dir / settings_filename_).string();
|
||||
|
||||
try {
|
||||
auto data = util::LoadFile(settings_path);
|
||||
if (!data.empty()) {
|
||||
std::istringstream ss(data);
|
||||
std::string line;
|
||||
while (std::getline(ss, line)) {
|
||||
auto eq = line.find('=');
|
||||
if (eq == std::string::npos)
|
||||
continue;
|
||||
auto key = line.substr(0, eq);
|
||||
auto val = line.substr(eq + 1);
|
||||
if (key == "font_global_scale")
|
||||
font_global_scale_ = std::stof(val);
|
||||
if (key == "autosave_enabled")
|
||||
autosave_enabled_ = (val == "1");
|
||||
if (key == "autosave_interval_secs")
|
||||
autosave_interval_secs_ = std::stof(val);
|
||||
}
|
||||
ImGui::GetIO().FontGlobalScale = font_global_scale_;
|
||||
}
|
||||
} catch (...) {
|
||||
// Could not load file, just use defaults.
|
||||
}
|
||||
}
|
||||
|
||||
void EditorManager::SaveUserSettings() {
|
||||
std::ostringstream ss;
|
||||
ss << "font_global_scale=" << font_global_scale_ << "\n";
|
||||
ss << "autosave_enabled=" << (autosave_enabled_ ? 1 : 0) << "\n";
|
||||
ss << "autosave_interval_secs=" << autosave_interval_secs_ << "\n";
|
||||
util::SaveFile(settings_filename_, ss.str());
|
||||
}
|
||||
|
||||
void EditorManager::RefreshWorkspacePresets() {
|
||||
// Safe clearing with error handling
|
||||
try {
|
||||
// Create a new vector instead of clearing to avoid corruption
|
||||
std::vector<std::string> new_presets;
|
||||
|
||||
// Try to read a simple index file of presets
|
||||
try {
|
||||
auto config_dir = util::PlatformPaths::GetConfigDirectory();
|
||||
if (config_dir.ok()) {
|
||||
std::string presets_path = (*config_dir / "workspace_presets.txt").string();
|
||||
auto data = util::LoadFile(presets_path);
|
||||
if (!data.empty()) {
|
||||
std::istringstream ss(data);
|
||||
std::string name;
|
||||
while (std::getline(ss, name)) {
|
||||
// Trim whitespace and validate
|
||||
name.erase(0, name.find_first_not_of(" \t\r\n"));
|
||||
name.erase(name.find_last_not_of(" \t\r\n") + 1);
|
||||
if (!name.empty() &&
|
||||
name.length() < 256) { // Reasonable length limit
|
||||
new_presets.emplace_back(std::move(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
LOG_WARN("EditorManager", "Failed to load workspace presets: %s", e.what());
|
||||
}
|
||||
|
||||
// Safely replace the vector
|
||||
workspace_presets_ = std::move(new_presets);
|
||||
workspace_presets_loaded_ = true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("EditorManager", "Error in RefreshWorkspacePresets: %s", e.what());
|
||||
// Ensure we have a valid empty vector
|
||||
workspace_presets_ = std::vector<std::string>();
|
||||
workspace_presets_loaded_ =
|
||||
true; // Mark as loaded even if empty to avoid retry
|
||||
}
|
||||
}
|
||||
|
||||
void EditorManager::SaveWorkspacePreset(const std::string& name) {
|
||||
if (name.empty())
|
||||
return;
|
||||
std::string ini_name = absl::StrCat("yaze_workspace_", name, ".ini");
|
||||
ImGui::SaveIniSettingsToDisk(ini_name.c_str());
|
||||
|
||||
// Ensure presets are loaded before updating
|
||||
if (!workspace_presets_loaded_) {
|
||||
RefreshWorkspacePresets();
|
||||
}
|
||||
|
||||
// Update index
|
||||
if (std::find(workspace_presets_.begin(), workspace_presets_.end(), name) ==
|
||||
workspace_presets_.end()) {
|
||||
workspace_presets_.emplace_back(name);
|
||||
try {
|
||||
std::ostringstream ss;
|
||||
for (const auto& n : workspace_presets_)
|
||||
ss << n << "\n";
|
||||
util::SaveFile("workspace_presets.txt", ss.str());
|
||||
} catch (const std::exception& e) {
|
||||
LOG_WARN("EditorManager", "Failed to save workspace presets: %s", e.what());
|
||||
}
|
||||
}
|
||||
last_workspace_preset_ = name;
|
||||
}
|
||||
|
||||
void EditorManager::LoadWorkspacePreset(const std::string& name) {
|
||||
if (name.empty())
|
||||
return;
|
||||
std::string ini_name = absl::StrCat("yaze_workspace_", name, ".ini");
|
||||
ImGui::LoadIniSettingsFromDisk(ini_name.c_str());
|
||||
last_workspace_preset_ = name;
|
||||
}
|
||||
|
||||
void EditorManager::InitializeTestSuites() {
|
||||
auto& test_manager = test::TestManager::Get();
|
||||
@@ -424,6 +308,11 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file
|
||||
#endif
|
||||
|
||||
// Load critical user settings first
|
||||
status_ = user_settings_.Load();
|
||||
if (!status_.ok()) {
|
||||
LOG_WARN("EditorManager", "Failed to load user settings: %s", status_.ToString().c_str());
|
||||
}
|
||||
|
||||
// Initialize welcome screen callbacks
|
||||
welcome_screen_.SetOpenRomCallback([this]() {
|
||||
status_ = LoadRom();
|
||||
@@ -499,6 +388,7 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file
|
||||
}
|
||||
});
|
||||
|
||||
// Load user settings - this must happen after context is initialized
|
||||
LoadUserSettings();
|
||||
|
||||
// Defer workspace presets loading to avoid initialization crashes
|
||||
@@ -682,7 +572,79 @@ void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& file
|
||||
"Maximize Window", ImGuiKey_F11, [this]() { MaximizeCurrentWindow(); });
|
||||
}
|
||||
|
||||
void EditorManager::OpenEditorAndCardsFromFlags(
|
||||
const std::string& editor_name, const std::string& cards_str) {
|
||||
if (editor_name.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("EditorManager", "Processing startup flags: editor='%s', cards='%s'",
|
||||
editor_name.c_str(), cards_str.c_str());
|
||||
|
||||
EditorType editor_type_to_open = EditorType::kUnknown;
|
||||
for (int i = 0; i < static_cast<int>(EditorType::kSettings); ++i) {
|
||||
if (GetEditorName(static_cast<EditorType>(i)) == editor_name) {
|
||||
editor_type_to_open = static_cast<EditorType>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (editor_type_to_open == EditorType::kUnknown) {
|
||||
LOG_WARN("EditorManager", "Unknown editor specified via flag: %s",
|
||||
editor_name.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Activate the main editor window
|
||||
if (current_editor_set_) {
|
||||
auto* editor = current_editor_set_->active_editors_[static_cast<int>(editor_type_to_open)];
|
||||
if (editor) {
|
||||
editor->set_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle specific cards for the Dungeon Editor
|
||||
if (editor_type_to_open == EditorType::kDungeon && !cards_str.empty()) {
|
||||
std::stringstream ss(cards_str);
|
||||
std::string card_name;
|
||||
while (std::getline(ss, card_name, ',')) {
|
||||
// Trim whitespace
|
||||
card_name.erase(0, card_name.find_first_not_of(" \t"));
|
||||
card_name.erase(card_name.find_last_not_of(" \t") + 1);
|
||||
|
||||
LOG_DEBUG("EditorManager", "Attempting to open card: '%s'",
|
||||
card_name.c_str());
|
||||
|
||||
if (card_name == "Rooms List") {
|
||||
current_editor_set_->dungeon_editor_.show_room_selector_ = true;
|
||||
} else if (card_name == "Room Matrix") {
|
||||
current_editor_set_->dungeon_editor_.show_room_matrix_ = true;
|
||||
} else if (card_name == "Entrances List") {
|
||||
current_editor_set_->dungeon_editor_.show_entrances_list_ = true;
|
||||
} else if (card_name == "Room Graphics") {
|
||||
current_editor_set_->dungeon_editor_.show_room_graphics_ = true;
|
||||
} else if (card_name == "Object Editor") {
|
||||
current_editor_set_->dungeon_editor_.show_object_editor_ = true;
|
||||
} else if (card_name == "Palette Editor") {
|
||||
current_editor_set_->dungeon_editor_.show_palette_editor_ = true;
|
||||
} else if (absl::StartsWith(card_name, "Room ")) {
|
||||
try {
|
||||
int room_id = std::stoi(card_name.substr(5));
|
||||
current_editor_set_->dungeon_editor_.add_room(room_id);
|
||||
} catch (const std::exception& e) {
|
||||
LOG_WARN("EditorManager", "Invalid room ID format: %s",
|
||||
card_name.c_str());
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("EditorManager", "Unknown card name for Dungeon Editor: %s",
|
||||
card_name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status EditorManager::Update() {
|
||||
|
||||
// Update timing manager for accurate delta time across the application
|
||||
// This fixes animation timing issues that occur when mouse isn't moving
|
||||
core::TimingManager::Get().Update();
|
||||
@@ -739,9 +701,9 @@ absl::Status EditorManager::Update() {
|
||||
}
|
||||
|
||||
// Autosave timer
|
||||
if (autosave_enabled_ && current_rom_ && current_rom_->dirty()) {
|
||||
if (user_settings_.prefs().autosave_enabled && current_rom_ && current_rom_->dirty()) {
|
||||
autosave_timer_ += ImGui::GetIO().DeltaTime;
|
||||
if (autosave_timer_ >= autosave_interval_secs_) {
|
||||
if (autosave_timer_ >= user_settings_.prefs().autosave_interval) {
|
||||
autosave_timer_ = 0.0f;
|
||||
Rom::SaveSettings s;
|
||||
s.backup = true;
|
||||
@@ -2122,18 +2084,18 @@ void EditorManager::DrawMenuBar() {
|
||||
ImGuiWindowFlags_AlwaysAutoResize);
|
||||
|
||||
// Lazy load workspace presets when UI is accessed
|
||||
if (!workspace_presets_loaded_) {
|
||||
if (!workspace_manager_.workspace_presets_loaded()) {
|
||||
RefreshWorkspacePresets();
|
||||
}
|
||||
|
||||
for (const auto& name : workspace_presets_) {
|
||||
for (const auto& name : workspace_manager_.workspace_presets()) {
|
||||
if (Selectable(name.c_str())) {
|
||||
LoadWorkspacePreset(name);
|
||||
toast_manager_.Show("Preset loaded", editor::ToastType::kSuccess);
|
||||
show_load_workspace_preset_ = false;
|
||||
}
|
||||
}
|
||||
if (workspace_presets_.empty())
|
||||
if (workspace_manager_.workspace_presets().empty())
|
||||
Text("No presets found");
|
||||
End();
|
||||
}
|
||||
@@ -2180,7 +2142,7 @@ absl::Status EditorManager::LoadRom() {
|
||||
current_editor_set_ = &target_session->editors;
|
||||
} else {
|
||||
// Create new session only if no empty ones exist
|
||||
sessions_.emplace_back(std::move(temp_rom));
|
||||
sessions_.emplace_back(std::move(temp_rom), &user_settings_);
|
||||
RomSession& session = sessions_.back();
|
||||
session.filepath = file_name; // Store filepath for duplicate detection
|
||||
|
||||
@@ -2269,8 +2231,8 @@ absl::Status EditorManager::SaveRom() {
|
||||
SaveAllGraphicsData(*current_rom_, gfx::Arena::Get().gfx_sheets()));
|
||||
|
||||
Rom::SaveSettings settings;
|
||||
settings.backup = backup_rom_;
|
||||
settings.save_new = save_new_auto_;
|
||||
settings.backup = user_settings_.prefs().backup_rom;
|
||||
settings.save_new = user_settings_.prefs().save_new_auto;
|
||||
return current_rom_->SaveToFile(settings);
|
||||
}
|
||||
|
||||
@@ -2297,7 +2259,7 @@ absl::Status EditorManager::SaveRomAs(const std::string& filename) {
|
||||
|
||||
// Create save settings with custom filename
|
||||
Rom::SaveSettings settings;
|
||||
settings.backup = backup_rom_;
|
||||
settings.backup = user_settings_.prefs().backup_rom;
|
||||
settings.save_new = false; // Don't auto-generate name, use provided filename
|
||||
settings.filename = filename;
|
||||
|
||||
@@ -2331,7 +2293,7 @@ absl::Status EditorManager::OpenRomOrProject(const std::string& filename) {
|
||||
} else {
|
||||
Rom temp_rom;
|
||||
RETURN_IF_ERROR(temp_rom.LoadFromFile(filename));
|
||||
sessions_.emplace_back(std::move(temp_rom));
|
||||
sessions_.emplace_back(std::move(temp_rom), &user_settings_);
|
||||
RomSession& session = sessions_.back();
|
||||
for (auto* editor : session.editors.active_editors_) {
|
||||
editor->set_context(&context_);
|
||||
@@ -2394,7 +2356,7 @@ absl::Status EditorManager::OpenProject() {
|
||||
}
|
||||
}
|
||||
|
||||
sessions_.emplace_back(std::move(temp_rom));
|
||||
sessions_.emplace_back(std::move(temp_rom), &user_settings_);
|
||||
RomSession& session = sessions_.back();
|
||||
for (auto* editor : session.editors.active_editors_) {
|
||||
editor->set_context(&context_);
|
||||
@@ -2427,11 +2389,11 @@ absl::Status EditorManager::OpenProject() {
|
||||
}
|
||||
|
||||
// Apply workspace settings
|
||||
font_global_scale_ = current_project_.workspace_settings.font_global_scale;
|
||||
autosave_enabled_ = current_project_.workspace_settings.autosave_enabled;
|
||||
autosave_interval_secs_ =
|
||||
user_settings_.prefs().font_global_scale = current_project_.workspace_settings.font_global_scale;
|
||||
user_settings_.prefs().autosave_enabled = current_project_.workspace_settings.autosave_enabled;
|
||||
user_settings_.prefs().autosave_interval =
|
||||
current_project_.workspace_settings.autosave_interval_secs;
|
||||
ImGui::GetIO().FontGlobalScale = font_global_scale_;
|
||||
ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
|
||||
|
||||
// Add to recent files
|
||||
auto& manager = core::RecentFilesManager::GetInstance();
|
||||
@@ -2457,10 +2419,10 @@ absl::Status EditorManager::SaveProject() {
|
||||
current_project_.feature_flags = sessions_[session_idx].feature_flags;
|
||||
}
|
||||
|
||||
current_project_.workspace_settings.font_global_scale = font_global_scale_;
|
||||
current_project_.workspace_settings.autosave_enabled = autosave_enabled_;
|
||||
current_project_.workspace_settings.font_global_scale = user_settings_.prefs().font_global_scale;
|
||||
current_project_.workspace_settings.autosave_enabled = user_settings_.prefs().autosave_enabled;
|
||||
current_project_.workspace_settings.autosave_interval_secs =
|
||||
autosave_interval_secs_;
|
||||
user_settings_.prefs().autosave_interval;
|
||||
|
||||
// Save recent files
|
||||
auto& manager = core::RecentFilesManager::GetInstance();
|
||||
@@ -2578,6 +2540,9 @@ void EditorManager::CreateNewSession() {
|
||||
// Create a blank session
|
||||
sessions_.emplace_back();
|
||||
RomSession& session = sessions_.back();
|
||||
|
||||
// Set user settings for the blank session
|
||||
session.editors.set_user_settings(&user_settings_);
|
||||
|
||||
// Wire editor contexts for new session
|
||||
for (auto* editor : session.editors.active_editors_) {
|
||||
@@ -2606,7 +2571,7 @@ void EditorManager::DuplicateCurrentSession() {
|
||||
|
||||
// Create a copy of the current ROM
|
||||
Rom rom_copy = *current_rom_;
|
||||
sessions_.emplace_back(std::move(rom_copy));
|
||||
sessions_.emplace_back(std::move(rom_copy), &user_settings_);
|
||||
RomSession& session = sessions_.back();
|
||||
|
||||
// Wire editor contexts
|
||||
@@ -3282,11 +3247,11 @@ void EditorManager::DrawLayoutPresets() {
|
||||
ImGui::Text("%s Custom Presets", ICON_MD_BOOKMARK);
|
||||
|
||||
// Lazy load workspace presets when UI is accessed
|
||||
if (!workspace_presets_loaded_) {
|
||||
if (!workspace_manager_.workspace_presets_loaded()) {
|
||||
RefreshWorkspacePresets();
|
||||
}
|
||||
|
||||
for (const auto& preset : workspace_presets_) {
|
||||
for (const auto& preset : workspace_manager_.workspace_presets()) {
|
||||
if (ImGui::Button(
|
||||
absl::StrFormat("%s %s", ICON_MD_BOOKMARK, preset.c_str())
|
||||
.c_str(),
|
||||
@@ -3298,7 +3263,7 @@ void EditorManager::DrawLayoutPresets() {
|
||||
}
|
||||
}
|
||||
|
||||
if (workspace_presets_.empty()) {
|
||||
if (workspace_manager_.workspace_presets().empty()) {
|
||||
ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f),
|
||||
"No custom presets saved");
|
||||
}
|
||||
@@ -3330,26 +3295,6 @@ void EditorManager::RenameSession(size_t index, const std::string& new_name) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string EditorManager::GenerateUniqueEditorTitle(EditorType type,
|
||||
size_t session_index) {
|
||||
std::string base_name = GetEditorName(type);
|
||||
|
||||
if (sessions_.size() <= 1) {
|
||||
return base_name; // No need for session identifier with single session
|
||||
}
|
||||
|
||||
// Add session identifier
|
||||
const auto& session = sessions_[session_index];
|
||||
std::string session_name = session.GetDisplayName();
|
||||
|
||||
// Truncate long session names
|
||||
if (session_name.length() > 15) {
|
||||
session_name = session_name.substr(0, 12) + "...";
|
||||
}
|
||||
|
||||
return absl::StrFormat("%s (%s)", base_name.c_str(), session_name.c_str());
|
||||
}
|
||||
|
||||
void EditorManager::DrawSessionRenameDialog() {
|
||||
if (!show_session_rename_dialog_)
|
||||
return;
|
||||
@@ -3438,5 +3383,27 @@ void EditorManager::SwitchToEditor(EditorType editor_type) {
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// User Settings Management
|
||||
// ============================================================================
|
||||
|
||||
void EditorManager::LoadUserSettings() {
|
||||
// Apply font scale after loading
|
||||
ImGui::GetIO().FontGlobalScale = user_settings_.prefs().font_global_scale;
|
||||
|
||||
// Apply welcome screen preference
|
||||
if (!user_settings_.prefs().show_welcome_on_startup) {
|
||||
show_welcome_screen_ = false;
|
||||
welcome_screen_manually_closed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void EditorManager::SaveUserSettings() {
|
||||
auto status = user_settings_.Save();
|
||||
if (!status.ok()) {
|
||||
LOG_WARN("EditorManager", "Failed to save user settings: %s", status.ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef YAZE_APP_EDITOR_EDITOR_MANAGER_H
|
||||
#define YAZE_APP_EDITOR_EDITOR_MANAGER_H
|
||||
|
||||
#include "editor/system/user_settings.h"
|
||||
#include "editor/ui/workspace_manager.h"
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
@@ -51,7 +53,7 @@ namespace editor {
|
||||
*/
|
||||
class EditorSet {
|
||||
public:
|
||||
explicit EditorSet(Rom* rom = nullptr)
|
||||
explicit EditorSet(Rom* rom = nullptr, UserSettings* user_settings = nullptr)
|
||||
: assembly_editor_(rom),
|
||||
dungeon_editor_(rom),
|
||||
graphics_editor_(rom),
|
||||
@@ -60,7 +62,7 @@ class EditorSet {
|
||||
palette_editor_(rom),
|
||||
screen_editor_(rom),
|
||||
sprite_editor_(rom),
|
||||
settings_editor_(rom),
|
||||
settings_editor_(rom, user_settings),
|
||||
message_editor_(rom),
|
||||
memory_editor_(rom) {
|
||||
active_editors_ = {&overworld_editor_, &dungeon_editor_, &graphics_editor_,
|
||||
@@ -68,6 +70,10 @@ class EditorSet {
|
||||
&music_editor_, &screen_editor_, &settings_editor_,
|
||||
&assembly_editor_};
|
||||
}
|
||||
|
||||
void set_user_settings(UserSettings* settings) {
|
||||
settings_editor_.set_user_settings(settings);
|
||||
}
|
||||
|
||||
AssemblyEditor assembly_editor_;
|
||||
DungeonEditorV2 dungeon_editor_;
|
||||
@@ -97,7 +103,7 @@ class EditorSet {
|
||||
*/
|
||||
class EditorManager {
|
||||
public:
|
||||
EditorManager() {
|
||||
EditorManager() : blank_editor_set_(nullptr, &user_settings_) {
|
||||
std::stringstream ss;
|
||||
ss << YAZE_VERSION_MAJOR << "." << YAZE_VERSION_MINOR << "."
|
||||
<< YAZE_VERSION_PATCH;
|
||||
@@ -106,6 +112,10 @@ class EditorManager {
|
||||
}
|
||||
|
||||
void Initialize(gfx::IRenderer* renderer, const std::string& filename = "");
|
||||
|
||||
// Processes startup flags to open a specific editor and cards.
|
||||
void OpenEditorAndCardsFromFlags(const std::string& editor_name,
|
||||
const std::string& cards_str);
|
||||
absl::Status Update();
|
||||
void DrawMenuBar();
|
||||
|
||||
@@ -118,6 +128,9 @@ class EditorManager {
|
||||
auto GetCurrentEditorSet() -> EditorSet* { return current_editor_set_; }
|
||||
auto overworld() -> yaze::zelda3::Overworld* { return ¤t_editor_set_->overworld_editor_.overworld(); }
|
||||
|
||||
// Session management helpers
|
||||
size_t GetCurrentSessionIndex() const;
|
||||
|
||||
// Get current session's feature flags (falls back to global if no session)
|
||||
core::FeatureFlags::Flags* GetCurrentFeatureFlags() {
|
||||
size_t current_index = GetCurrentSessionIndex();
|
||||
@@ -128,29 +141,71 @@ class EditorManager {
|
||||
}
|
||||
|
||||
void SetFontGlobalScale(float scale) {
|
||||
font_global_scale_ = scale;
|
||||
user_settings_.prefs().font_global_scale = scale;
|
||||
ImGui::GetIO().FontGlobalScale = scale;
|
||||
SaveUserSettings();
|
||||
}
|
||||
|
||||
void BuildModernMenu();
|
||||
|
||||
// User settings helpers
|
||||
void LoadUserSettings();
|
||||
void SaveUserSettings();
|
||||
|
||||
// Workspace management (delegates to WorkspaceManager)
|
||||
void RefreshWorkspacePresets() { workspace_manager_.RefreshPresets(); }
|
||||
void SaveWorkspacePreset(const std::string& name) { workspace_manager_.SaveWorkspacePreset(name); }
|
||||
void LoadWorkspacePreset(const std::string& name) { workspace_manager_.LoadWorkspacePreset(name); }
|
||||
|
||||
// Jump-to functionality for cross-editor navigation
|
||||
void JumpToDungeonRoom(int room_id);
|
||||
void JumpToOverworldMap(int map_id);
|
||||
void SwitchToEditor(EditorType editor_type);
|
||||
|
||||
// Session management
|
||||
void CreateNewSession();
|
||||
void DuplicateCurrentSession();
|
||||
void CloseCurrentSession();
|
||||
void RemoveSession(size_t index);
|
||||
void SwitchToSession(size_t index);
|
||||
size_t GetActiveSessionCount() const;
|
||||
|
||||
// Workspace layout management
|
||||
void SaveWorkspaceLayout();
|
||||
void LoadWorkspaceLayout();
|
||||
void ResetWorkspaceLayout();
|
||||
void ShowAllWindows();
|
||||
void HideAllWindows();
|
||||
void MaximizeCurrentWindow();
|
||||
void RestoreAllWindows();
|
||||
void CloseAllFloatingWindows();
|
||||
|
||||
// Layout presets
|
||||
void LoadDeveloperLayout();
|
||||
void LoadDesignerLayout();
|
||||
void LoadModderLayout();
|
||||
|
||||
// Helper methods
|
||||
std::string GenerateUniqueEditorTitle(EditorType type, size_t session_index) const;
|
||||
bool HasDuplicateSession(const std::string& filepath);
|
||||
void RenameSession(size_t index, const std::string& new_name);
|
||||
|
||||
private:
|
||||
void DrawWelcomeScreen();
|
||||
absl::Status DrawRomSelector();
|
||||
void DrawContextSensitiveCardControl(); // Card control for current editor
|
||||
void DrawSessionSwitcher();
|
||||
void DrawSessionManager();
|
||||
void DrawLayoutPresets();
|
||||
void DrawSessionRenameDialog();
|
||||
|
||||
absl::Status LoadRom();
|
||||
absl::Status LoadAssets();
|
||||
absl::Status SaveRom();
|
||||
absl::Status SaveRomAs(const std::string& filename);
|
||||
absl::Status OpenRomOrProject(const std::string& filename);
|
||||
|
||||
// Enhanced project management
|
||||
// Project and session management
|
||||
absl::Status CreateNewProject(
|
||||
const std::string& template_name = "Basic ROM Hack");
|
||||
absl::Status OpenProject();
|
||||
@@ -164,11 +219,6 @@ class EditorManager {
|
||||
void InitializeTestSuites();
|
||||
|
||||
bool quit_ = false;
|
||||
bool backup_rom_ = false;
|
||||
bool save_new_auto_ = true;
|
||||
bool autosave_enabled_ = false;
|
||||
float autosave_interval_secs_ = 120.0f;
|
||||
float autosave_timer_ = 0.0f;
|
||||
bool new_project_menu = false;
|
||||
|
||||
bool show_emulator_ = false;
|
||||
@@ -226,13 +276,6 @@ class EditorManager {
|
||||
#endif
|
||||
|
||||
std::string version_ = "";
|
||||
std::string settings_filename_ = "settings.ini";
|
||||
float font_global_scale_ = 1.0f;
|
||||
std::vector<std::string> workspace_presets_;
|
||||
std::string last_workspace_preset_ = "";
|
||||
std::string status_message_ = "";
|
||||
bool workspace_presets_loaded_ = false;
|
||||
|
||||
absl::Status status_;
|
||||
emu::Emulator emulator_;
|
||||
|
||||
@@ -244,7 +287,8 @@ class EditorManager {
|
||||
core::FeatureFlags::Flags feature_flags; // Per-session feature flags
|
||||
|
||||
RomSession() = default;
|
||||
explicit RomSession(Rom&& r) : rom(std::move(r)), editors(&rom) {
|
||||
explicit RomSession(Rom&& r, UserSettings* user_settings = nullptr)
|
||||
: rom(std::move(r)), editors(&rom, user_settings) {
|
||||
filepath = rom.filename();
|
||||
// Initialize with default feature flags
|
||||
feature_flags = core::FeatureFlags::Flags{};
|
||||
@@ -272,49 +316,10 @@ class EditorManager {
|
||||
std::unique_ptr<PopupManager> popup_manager_;
|
||||
ToastManager toast_manager_;
|
||||
MenuBuilder menu_builder_;
|
||||
|
||||
// Settings helpers
|
||||
void LoadUserSettings();
|
||||
void SaveUserSettings();
|
||||
|
||||
void RefreshWorkspacePresets();
|
||||
void SaveWorkspacePreset(const std::string& name);
|
||||
void LoadWorkspacePreset(const std::string& name);
|
||||
|
||||
// Workspace management
|
||||
void CreateNewSession();
|
||||
void DuplicateCurrentSession();
|
||||
void CloseCurrentSession();
|
||||
void RemoveSession(size_t index);
|
||||
void SwitchToSession(size_t index);
|
||||
size_t GetCurrentSessionIndex() const;
|
||||
size_t GetActiveSessionCount() const;
|
||||
void ResetWorkspaceLayout();
|
||||
|
||||
// Multi-session editor management
|
||||
std::string GenerateUniqueEditorTitle(EditorType type,
|
||||
size_t session_index) const;
|
||||
void SaveWorkspaceLayout();
|
||||
void LoadWorkspaceLayout();
|
||||
void ShowAllWindows();
|
||||
void HideAllWindows();
|
||||
void MaximizeCurrentWindow();
|
||||
void RestoreAllWindows();
|
||||
void CloseAllFloatingWindows();
|
||||
void LoadDeveloperLayout();
|
||||
void LoadDesignerLayout();
|
||||
void LoadModderLayout();
|
||||
|
||||
// Session management helpers
|
||||
bool HasDuplicateSession(const std::string& filepath);
|
||||
void RenameSession(size_t index, const std::string& new_name);
|
||||
std::string GenerateUniqueEditorTitle(EditorType type, size_t session_index);
|
||||
|
||||
// UI drawing helpers
|
||||
void DrawSessionSwitcher();
|
||||
void DrawSessionManager();
|
||||
void DrawLayoutPresets();
|
||||
void DrawSessionRenameDialog();
|
||||
UserSettings user_settings_;
|
||||
WorkspaceManager workspace_manager_{&toast_manager_};
|
||||
|
||||
float autosave_timer_ = 0.0f;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
@@ -85,8 +85,6 @@ void GraphicsEditor::Initialize() {
|
||||
.visibility_flag = &show_prototype_viewer_,
|
||||
.priority = 40
|
||||
});
|
||||
|
||||
printf("[GraphicsEditor] Registered 4 cards with EditorCardManager\n");
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::Load() {
|
||||
|
||||
@@ -187,9 +187,6 @@ absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
|
||||
}
|
||||
|
||||
void PaletteEditor::Initialize() {
|
||||
// PaletteEditor uses tabs within a single window, not separate cards
|
||||
// So we don't register individual cards - the whole editor is one unit
|
||||
printf("[PaletteEditor] No cards to register (uses internal tabs)\n");
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::Load() {
|
||||
|
||||
@@ -82,8 +82,6 @@ void ScreenEditor::Initialize() {
|
||||
.visibility_flag = &show_naming_screen_,
|
||||
.priority = 50
|
||||
});
|
||||
|
||||
printf("[ScreenEditor] Registered 5 cards with EditorCardManager\n");
|
||||
}
|
||||
|
||||
absl::Status ScreenEditor::Load() {
|
||||
|
||||
@@ -100,8 +100,6 @@ void MessageEditor::Initialize() {
|
||||
.priority = 40
|
||||
});
|
||||
|
||||
printf("[MessageEditor] Registered 4 cards with EditorCardManager\n");
|
||||
|
||||
for (int i = 0; i < kWidthArraySize; i++) {
|
||||
message_preview_.width_array[i] = rom()->data()[kCharactersWidth + i];
|
||||
}
|
||||
|
||||
@@ -120,8 +120,6 @@ void OverworldEditor::Initialize() {
|
||||
.priority = 70
|
||||
});
|
||||
|
||||
printf("[OverworldEditor] Registered 7 cards with EditorCardManager\n");
|
||||
|
||||
// Original initialization code below:
|
||||
// Initialize MapPropertiesSystem with canvas and bitmap data
|
||||
map_properties_system_ = std::make_unique<MapPropertiesSystem>(
|
||||
|
||||
@@ -47,8 +47,6 @@ void SpriteEditor::Initialize() {
|
||||
.visibility_flag = &show_custom_editor_,
|
||||
.priority = 20
|
||||
});
|
||||
|
||||
printf("[SpriteEditor] Registered 2 cards with EditorCardManager\n");
|
||||
}
|
||||
|
||||
absl::Status SpriteEditor::Load() {
|
||||
|
||||
@@ -162,60 +162,81 @@ void SettingsEditor::DrawThemeSettings() {
|
||||
void SettingsEditor::DrawEditorBehavior() {
|
||||
using namespace ImGui;
|
||||
|
||||
if (!user_settings_) {
|
||||
Text("No user settings available");
|
||||
return;
|
||||
}
|
||||
|
||||
Text("%s Editor Behavior Settings", ICON_MD_TUNE);
|
||||
Separator();
|
||||
|
||||
// Autosave settings
|
||||
if (CollapsingHeader(ICON_MD_SAVE " Auto-Save", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
static bool autosave_enabled = true;
|
||||
Checkbox("Enable Auto-Save", &autosave_enabled);
|
||||
if (Checkbox("Enable Auto-Save", &user_settings_->prefs().autosave_enabled)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
if (autosave_enabled) {
|
||||
static int autosave_interval = 300;
|
||||
SliderInt("Interval (seconds)", &autosave_interval, 60, 600);
|
||||
if (user_settings_->prefs().autosave_enabled) {
|
||||
int interval = static_cast<int>(user_settings_->prefs().autosave_interval);
|
||||
if (SliderInt("Interval (seconds)", &interval, 60, 600)) {
|
||||
user_settings_->prefs().autosave_interval = static_cast<float>(interval);
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
static bool backup_before_save = true;
|
||||
Checkbox("Create Backup Before Save", &backup_before_save);
|
||||
if (Checkbox("Create Backup Before Save", &user_settings_->prefs().backup_before_save)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recent files
|
||||
if (CollapsingHeader(ICON_MD_HISTORY " Recent Files")) {
|
||||
static int recent_files_limit = 10;
|
||||
SliderInt("Recent Files Limit", &recent_files_limit, 5, 50);
|
||||
if (SliderInt("Recent Files Limit", &user_settings_->prefs().recent_files_limit, 5, 50)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
}
|
||||
|
||||
// Editor defaults
|
||||
if (CollapsingHeader(ICON_MD_EDIT " Default Editor")) {
|
||||
Text("Editor to open on ROM load:");
|
||||
static int default_editor = 0;
|
||||
const char* editors[] = { "None", "Overworld", "Dungeon", "Graphics" };
|
||||
Combo("##DefaultEditor", &default_editor, editors, IM_ARRAYSIZE(editors));
|
||||
if (Combo("##DefaultEditor", &user_settings_->prefs().default_editor, editors, IM_ARRAYSIZE(editors))) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsEditor::DrawPerformanceSettings() {
|
||||
using namespace ImGui;
|
||||
|
||||
if (!user_settings_) {
|
||||
Text("No user settings available");
|
||||
return;
|
||||
}
|
||||
|
||||
Text("%s Performance Settings", ICON_MD_SPEED);
|
||||
Separator();
|
||||
|
||||
// Graphics settings
|
||||
if (CollapsingHeader(ICON_MD_IMAGE " Graphics", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
static bool vsync = true;
|
||||
Checkbox("V-Sync", &vsync);
|
||||
if (Checkbox("V-Sync", &user_settings_->prefs().vsync)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
static int target_fps = 60;
|
||||
SliderInt("Target FPS", &target_fps, 30, 144);
|
||||
if (SliderInt("Target FPS", &user_settings_->prefs().target_fps, 30, 144)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
}
|
||||
|
||||
// Memory settings
|
||||
if (CollapsingHeader(ICON_MD_MEMORY " Memory")) {
|
||||
static int cache_size = 512;
|
||||
SliderInt("Cache Size (MB)", &cache_size, 128, 2048);
|
||||
if (SliderInt("Cache Size (MB)", &user_settings_->prefs().cache_size_mb, 128, 2048)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
static int undo_size = 50;
|
||||
SliderInt("Undo History", &undo_size, 10, 200);
|
||||
if (SliderInt("Undo History", &user_settings_->prefs().undo_history_size, 10, 200)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
}
|
||||
|
||||
Separator();
|
||||
@@ -226,46 +247,67 @@ void SettingsEditor::DrawPerformanceSettings() {
|
||||
void SettingsEditor::DrawAIAgentSettings() {
|
||||
using namespace ImGui;
|
||||
|
||||
if (!user_settings_) {
|
||||
Text("No user settings available");
|
||||
return;
|
||||
}
|
||||
|
||||
Text("%s AI Agent Configuration", ICON_MD_SMART_TOY);
|
||||
Separator();
|
||||
|
||||
// Provider selection
|
||||
if (CollapsingHeader(ICON_MD_CLOUD " AI Provider", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
static int provider = 0;
|
||||
const char* providers[] = { "Ollama (Local)", "Gemini (Cloud)", "Mock (Testing)" };
|
||||
Combo("Provider", &provider, providers, IM_ARRAYSIZE(providers));
|
||||
if (Combo("Provider", &user_settings_->prefs().ai_provider, providers, IM_ARRAYSIZE(providers))) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
Spacing();
|
||||
|
||||
if (provider == 0) { // Ollama
|
||||
static char ollama_url[256] = "http://localhost:11434";
|
||||
InputText("URL", ollama_url, IM_ARRAYSIZE(ollama_url));
|
||||
} else if (provider == 1) { // Gemini
|
||||
static char api_key[128] = "";
|
||||
InputText("API Key", api_key, IM_ARRAYSIZE(api_key), ImGuiInputTextFlags_Password);
|
||||
if (user_settings_->prefs().ai_provider == 0) { // Ollama
|
||||
char url_buffer[256];
|
||||
strncpy(url_buffer, user_settings_->prefs().ollama_url.c_str(), sizeof(url_buffer) - 1);
|
||||
url_buffer[sizeof(url_buffer) - 1] = '\0';
|
||||
if (InputText("URL", url_buffer, IM_ARRAYSIZE(url_buffer))) {
|
||||
user_settings_->prefs().ollama_url = url_buffer;
|
||||
user_settings_->Save();
|
||||
}
|
||||
} else if (user_settings_->prefs().ai_provider == 1) { // Gemini
|
||||
char key_buffer[128];
|
||||
strncpy(key_buffer, user_settings_->prefs().gemini_api_key.c_str(), sizeof(key_buffer) - 1);
|
||||
key_buffer[sizeof(key_buffer) - 1] = '\0';
|
||||
if (InputText("API Key", key_buffer, IM_ARRAYSIZE(key_buffer), ImGuiInputTextFlags_Password)) {
|
||||
user_settings_->prefs().gemini_api_key = key_buffer;
|
||||
user_settings_->Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Model parameters
|
||||
if (CollapsingHeader(ICON_MD_TUNE " Model Parameters")) {
|
||||
static float temperature = 0.7f;
|
||||
SliderFloat("Temperature", &temperature, 0.0f, 2.0f);
|
||||
if (SliderFloat("Temperature", &user_settings_->prefs().ai_temperature, 0.0f, 2.0f)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
TextDisabled("Higher = more creative");
|
||||
|
||||
static int max_tokens = 2048;
|
||||
SliderInt("Max Tokens", &max_tokens, 256, 8192);
|
||||
if (SliderInt("Max Tokens", &user_settings_->prefs().ai_max_tokens, 256, 8192)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
}
|
||||
|
||||
// Agent behavior
|
||||
if (CollapsingHeader(ICON_MD_PSYCHOLOGY " Behavior")) {
|
||||
static bool proactive = true;
|
||||
Checkbox("Proactive Suggestions", &proactive);
|
||||
if (Checkbox("Proactive Suggestions", &user_settings_->prefs().ai_proactive)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
static bool auto_learn = true;
|
||||
Checkbox("Auto-Learn Preferences", &auto_learn);
|
||||
if (Checkbox("Auto-Learn Preferences", &user_settings_->prefs().ai_auto_learn)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
static bool multimodal = true;
|
||||
Checkbox("Enable Vision/Multimodal", &multimodal);
|
||||
if (Checkbox("Enable Vision/Multimodal", &user_settings_->prefs().ai_multimodal)) {
|
||||
user_settings_->Save();
|
||||
}
|
||||
}
|
||||
|
||||
// z3ed CLI logging settings
|
||||
@@ -273,21 +315,12 @@ void SettingsEditor::DrawAIAgentSettings() {
|
||||
Text("Configure z3ed command-line logging behavior");
|
||||
Spacing();
|
||||
|
||||
// Declare all static variables first
|
||||
static int log_level = 1; // 0=Debug, 1=Info, 2=Warning, 3=Error, 4=Fatal
|
||||
static bool log_to_file = false;
|
||||
static char log_file_path[512] = "";
|
||||
static bool log_ai_requests = true;
|
||||
static bool log_rom_operations = true;
|
||||
static bool log_gui_automation = true;
|
||||
static bool log_proposals = true;
|
||||
|
||||
// Log level selection
|
||||
const char* log_levels[] = { "Debug (Verbose)", "Info (Normal)", "Warning (Quiet)", "Error (Critical)", "Fatal Only" };
|
||||
if (Combo("Log Level", &log_level, log_levels, IM_ARRAYSIZE(log_levels))) {
|
||||
if (Combo("Log Level", &user_settings_->prefs().log_level, log_levels, IM_ARRAYSIZE(log_levels))) {
|
||||
// Apply log level immediately using existing LogManager
|
||||
util::LogLevel level;
|
||||
switch (log_level) {
|
||||
switch (user_settings_->prefs().log_level) {
|
||||
case 0: level = util::LogLevel::YAZE_DEBUG; break;
|
||||
case 1: level = util::LogLevel::INFO; break;
|
||||
case 2: level = util::LogLevel::WARNING; break;
|
||||
@@ -298,13 +331,14 @@ void SettingsEditor::DrawAIAgentSettings() {
|
||||
|
||||
// Get current categories
|
||||
std::set<std::string> categories;
|
||||
if (log_ai_requests) categories.insert("AI");
|
||||
if (log_rom_operations) categories.insert("ROM");
|
||||
if (log_gui_automation) categories.insert("GUI");
|
||||
if (log_proposals) categories.insert("Proposals");
|
||||
if (user_settings_->prefs().log_ai_requests) categories.insert("AI");
|
||||
if (user_settings_->prefs().log_rom_operations) categories.insert("ROM");
|
||||
if (user_settings_->prefs().log_gui_automation) categories.insert("GUI");
|
||||
if (user_settings_->prefs().log_proposals) categories.insert("Proposals");
|
||||
|
||||
// Reconfigure with new level
|
||||
util::LogManager::instance().configure(level, std::string(log_file_path), categories);
|
||||
util::LogManager::instance().configure(level, user_settings_->prefs().log_file_path, categories);
|
||||
user_settings_->Save();
|
||||
Text("✓ Log level applied");
|
||||
}
|
||||
TextDisabled("Controls verbosity of YAZE and z3ed output");
|
||||
@@ -312,36 +346,41 @@ void SettingsEditor::DrawAIAgentSettings() {
|
||||
Spacing();
|
||||
|
||||
// Logging targets
|
||||
|
||||
if (Checkbox("Log to File", &log_to_file)) {
|
||||
if (log_to_file) {
|
||||
if (Checkbox("Log to File", &user_settings_->prefs().log_to_file)) {
|
||||
if (user_settings_->prefs().log_to_file) {
|
||||
// Set default path if empty
|
||||
if (strlen(log_file_path) == 0) {
|
||||
if (user_settings_->prefs().log_file_path.empty()) {
|
||||
const char* home = std::getenv("HOME");
|
||||
if (home) {
|
||||
snprintf(log_file_path, sizeof(log_file_path), "%s/.yaze/logs/yaze.log", home);
|
||||
user_settings_->prefs().log_file_path = std::string(home) + "/.yaze/logs/yaze.log";
|
||||
}
|
||||
}
|
||||
|
||||
// Enable file logging
|
||||
std::set<std::string> categories;
|
||||
util::LogLevel level = static_cast<util::LogLevel>(log_level);
|
||||
util::LogManager::instance().configure(level, std::string(log_file_path), categories);
|
||||
util::LogLevel level = static_cast<util::LogLevel>(user_settings_->prefs().log_level);
|
||||
util::LogManager::instance().configure(level, user_settings_->prefs().log_file_path, categories);
|
||||
} else {
|
||||
// Disable file logging
|
||||
std::set<std::string> categories;
|
||||
util::LogLevel level = static_cast<util::LogLevel>(log_level);
|
||||
util::LogLevel level = static_cast<util::LogLevel>(user_settings_->prefs().log_level);
|
||||
util::LogManager::instance().configure(level, "", categories);
|
||||
}
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
if (log_to_file) {
|
||||
if (user_settings_->prefs().log_to_file) {
|
||||
Indent();
|
||||
if (InputText("Log File", log_file_path, IM_ARRAYSIZE(log_file_path))) {
|
||||
char path_buffer[512];
|
||||
strncpy(path_buffer, user_settings_->prefs().log_file_path.c_str(), sizeof(path_buffer) - 1);
|
||||
path_buffer[sizeof(path_buffer) - 1] = '\0';
|
||||
if (InputText("Log File", path_buffer, IM_ARRAYSIZE(path_buffer))) {
|
||||
// Update log file path
|
||||
user_settings_->prefs().log_file_path = path_buffer;
|
||||
std::set<std::string> categories;
|
||||
util::LogLevel level = static_cast<util::LogLevel>(log_level);
|
||||
util::LogManager::instance().configure(level, std::string(log_file_path), categories);
|
||||
util::LogLevel level = static_cast<util::LogLevel>(user_settings_->prefs().log_level);
|
||||
util::LogManager::instance().configure(level, user_settings_->prefs().log_file_path, categories);
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
TextDisabled("Log file path (supports ~ for home directory)");
|
||||
@@ -358,40 +397,43 @@ void SettingsEditor::DrawAIAgentSettings() {
|
||||
|
||||
bool categories_changed = false;
|
||||
|
||||
categories_changed |= Checkbox("AI API Requests", &log_ai_requests);
|
||||
categories_changed |= Checkbox("ROM Operations", &log_rom_operations);
|
||||
categories_changed |= Checkbox("GUI Automation", &log_gui_automation);
|
||||
categories_changed |= Checkbox("Proposal Generation", &log_proposals);
|
||||
categories_changed |= Checkbox("AI API Requests", &user_settings_->prefs().log_ai_requests);
|
||||
categories_changed |= Checkbox("ROM Operations", &user_settings_->prefs().log_rom_operations);
|
||||
categories_changed |= Checkbox("GUI Automation", &user_settings_->prefs().log_gui_automation);
|
||||
categories_changed |= Checkbox("Proposal Generation", &user_settings_->prefs().log_proposals);
|
||||
|
||||
if (categories_changed) {
|
||||
// Rebuild category set
|
||||
std::set<std::string> categories;
|
||||
if (log_ai_requests) categories.insert("AI");
|
||||
if (log_rom_operations) categories.insert("ROM");
|
||||
if (log_gui_automation) categories.insert("GUI");
|
||||
if (log_proposals) categories.insert("Proposals");
|
||||
if (user_settings_->prefs().log_ai_requests) categories.insert("AI");
|
||||
if (user_settings_->prefs().log_rom_operations) categories.insert("ROM");
|
||||
if (user_settings_->prefs().log_gui_automation) categories.insert("GUI");
|
||||
if (user_settings_->prefs().log_proposals) categories.insert("Proposals");
|
||||
|
||||
// Reconfigure LogManager
|
||||
util::LogLevel level = static_cast<util::LogLevel>(log_level);
|
||||
util::LogManager::instance().configure(level, log_to_file ? std::string(log_file_path) : "", categories);
|
||||
util::LogLevel level = static_cast<util::LogLevel>(user_settings_->prefs().log_level);
|
||||
util::LogManager::instance().configure(level,
|
||||
user_settings_->prefs().log_to_file ? user_settings_->prefs().log_file_path : "",
|
||||
categories);
|
||||
user_settings_->Save();
|
||||
}
|
||||
|
||||
Spacing();
|
||||
|
||||
// Quick actions
|
||||
if (Button(ICON_MD_DELETE " Clear Logs")) {
|
||||
if (log_to_file && strlen(log_file_path) > 0) {
|
||||
std::filesystem::path path(log_file_path);
|
||||
if (user_settings_->prefs().log_to_file && !user_settings_->prefs().log_file_path.empty()) {
|
||||
std::filesystem::path path(user_settings_->prefs().log_file_path);
|
||||
if (std::filesystem::exists(path)) {
|
||||
std::filesystem::remove(path);
|
||||
LOG_DEBUG("Settings", "Log file cleared: %s", log_file_path);
|
||||
LOG_DEBUG("Settings", "Log file cleared: %s", user_settings_->prefs().log_file_path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
SameLine();
|
||||
if (Button(ICON_MD_FOLDER_OPEN " Open Log Directory")) {
|
||||
if (log_to_file && strlen(log_file_path) > 0) {
|
||||
std::filesystem::path path(log_file_path);
|
||||
if (user_settings_->prefs().log_to_file && !user_settings_->prefs().log_file_path.empty()) {
|
||||
std::filesystem::path path(user_settings_->prefs().log_file_path);
|
||||
std::filesystem::path dir = path.parent_path();
|
||||
|
||||
// Platform-specific command to open directory
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/rom.h"
|
||||
#include "editor/system/user_settings.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -207,7 +208,8 @@ static void ShowExampleAppPropertyEditor(bool* p_open) {
|
||||
|
||||
class SettingsEditor : public Editor {
|
||||
public:
|
||||
explicit SettingsEditor(Rom* rom = nullptr) : rom_(rom) {
|
||||
explicit SettingsEditor(Rom* rom = nullptr, UserSettings* user_settings = nullptr)
|
||||
: rom_(rom), user_settings_(user_settings) {
|
||||
type_ = EditorType::kSettings;
|
||||
}
|
||||
|
||||
@@ -215,6 +217,8 @@ class SettingsEditor : public Editor {
|
||||
absl::Status Load() override;
|
||||
absl::Status Save() override { return absl::UnimplementedError("Save"); }
|
||||
absl::Status Update() override;
|
||||
|
||||
void set_user_settings(UserSettings* settings) { user_settings_ = settings; }
|
||||
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
|
||||
absl::Status Copy() override { return absl::UnimplementedError("Copy"); }
|
||||
absl::Status Paste() override { return absl::UnimplementedError("Paste"); }
|
||||
@@ -232,6 +236,7 @@ class SettingsEditor : public Editor {
|
||||
|
||||
private:
|
||||
Rom* rom_;
|
||||
UserSettings* user_settings_;
|
||||
void DrawGeneralSettings();
|
||||
void DrawKeyboardShortcuts();
|
||||
void DrawThemeSettings();
|
||||
|
||||
@@ -1,24 +1,177 @@
|
||||
#include "app/editor/system/user_settings.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/log.h"
|
||||
#include "util/platform_paths.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
UserSettings::UserSettings() {
|
||||
// TODO: Get platform-specific settings path
|
||||
settings_file_path_ = "yaze_settings.json";
|
||||
auto config_dir_status = util::PlatformPaths::GetConfigDirectory();
|
||||
if (config_dir_status.ok()) {
|
||||
settings_file_path_ = (*config_dir_status / "yaze_settings.ini").string();
|
||||
} else {
|
||||
LOG_WARN("UserSettings", "Could not determine config directory. Using local.");
|
||||
settings_file_path_ = "yaze_settings.ini";
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status UserSettings::Load() {
|
||||
// TODO: Load from JSON file
|
||||
try {
|
||||
auto data = util::LoadFile(settings_file_path_);
|
||||
if (data.empty()) {
|
||||
return absl::OkStatus(); // No settings file yet, use defaults.
|
||||
}
|
||||
|
||||
std::istringstream ss(data);
|
||||
std::string line;
|
||||
while (std::getline(ss, line)) {
|
||||
size_t eq_pos = line.find('=');
|
||||
if (eq_pos == std::string::npos) continue;
|
||||
|
||||
std::string key = line.substr(0, eq_pos);
|
||||
std::string val = line.substr(eq_pos + 1);
|
||||
|
||||
// General
|
||||
if (key == "font_global_scale") {
|
||||
prefs_.font_global_scale = std::stof(val);
|
||||
} else if (key == "backup_rom") {
|
||||
prefs_.backup_rom = (val == "1");
|
||||
} else if (key == "save_new_auto") {
|
||||
prefs_.save_new_auto = (val == "1");
|
||||
} else if (key == "autosave_enabled") {
|
||||
prefs_.autosave_enabled = (val == "1");
|
||||
} else if (key == "autosave_interval") {
|
||||
prefs_.autosave_interval = std::stof(val);
|
||||
} else if (key == "recent_files_limit") {
|
||||
prefs_.recent_files_limit = std::stoi(val);
|
||||
} else if (key == "last_rom_path") {
|
||||
prefs_.last_rom_path = val;
|
||||
} else if (key == "last_project_path") {
|
||||
prefs_.last_project_path = val;
|
||||
} else if (key == "show_welcome_on_startup") {
|
||||
prefs_.show_welcome_on_startup = (val == "1");
|
||||
} else if (key == "restore_last_session") {
|
||||
prefs_.restore_last_session = (val == "1");
|
||||
}
|
||||
// Editor Behavior
|
||||
else if (key == "backup_before_save") {
|
||||
prefs_.backup_before_save = (val == "1");
|
||||
} else if (key == "default_editor") {
|
||||
prefs_.default_editor = std::stoi(val);
|
||||
}
|
||||
// Performance
|
||||
else if (key == "vsync") {
|
||||
prefs_.vsync = (val == "1");
|
||||
} else if (key == "target_fps") {
|
||||
prefs_.target_fps = std::stoi(val);
|
||||
} else if (key == "cache_size_mb") {
|
||||
prefs_.cache_size_mb = std::stoi(val);
|
||||
} else if (key == "undo_history_size") {
|
||||
prefs_.undo_history_size = std::stoi(val);
|
||||
}
|
||||
// AI Agent
|
||||
else if (key == "ai_provider") {
|
||||
prefs_.ai_provider = std::stoi(val);
|
||||
} else if (key == "ollama_url") {
|
||||
prefs_.ollama_url = val;
|
||||
} else if (key == "gemini_api_key") {
|
||||
prefs_.gemini_api_key = val;
|
||||
} else if (key == "ai_temperature") {
|
||||
prefs_.ai_temperature = std::stof(val);
|
||||
} else if (key == "ai_max_tokens") {
|
||||
prefs_.ai_max_tokens = std::stoi(val);
|
||||
} else if (key == "ai_proactive") {
|
||||
prefs_.ai_proactive = (val == "1");
|
||||
} else if (key == "ai_auto_learn") {
|
||||
prefs_.ai_auto_learn = (val == "1");
|
||||
} else if (key == "ai_multimodal") {
|
||||
prefs_.ai_multimodal = (val == "1");
|
||||
}
|
||||
// CLI Logging
|
||||
else if (key == "log_level") {
|
||||
prefs_.log_level = std::stoi(val);
|
||||
} else if (key == "log_to_file") {
|
||||
prefs_.log_to_file = (val == "1");
|
||||
} else if (key == "log_file_path") {
|
||||
prefs_.log_file_path = val;
|
||||
} else if (key == "log_ai_requests") {
|
||||
prefs_.log_ai_requests = (val == "1");
|
||||
} else if (key == "log_rom_operations") {
|
||||
prefs_.log_rom_operations = (val == "1");
|
||||
} else if (key == "log_gui_automation") {
|
||||
prefs_.log_gui_automation = (val == "1");
|
||||
} else if (key == "log_proposals") {
|
||||
prefs_.log_proposals = (val == "1");
|
||||
}
|
||||
}
|
||||
ImGui::GetIO().FontGlobalScale = prefs_.font_global_scale;
|
||||
} catch (const std::exception& e) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to load user settings: %s", e.what()));
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status UserSettings::Save() {
|
||||
// TODO: Save to JSON file
|
||||
try {
|
||||
std::ostringstream ss;
|
||||
// General
|
||||
ss << "font_global_scale=" << prefs_.font_global_scale << "\n";
|
||||
ss << "backup_rom=" << (prefs_.backup_rom ? 1 : 0) << "\n";
|
||||
ss << "save_new_auto=" << (prefs_.save_new_auto ? 1 : 0) << "\n";
|
||||
ss << "autosave_enabled=" << (prefs_.autosave_enabled ? 1 : 0) << "\n";
|
||||
ss << "autosave_interval=" << prefs_.autosave_interval << "\n";
|
||||
ss << "recent_files_limit=" << prefs_.recent_files_limit << "\n";
|
||||
ss << "last_rom_path=" << prefs_.last_rom_path << "\n";
|
||||
ss << "last_project_path=" << prefs_.last_project_path << "\n";
|
||||
ss << "show_welcome_on_startup=" << (prefs_.show_welcome_on_startup ? 1 : 0) << "\n";
|
||||
ss << "restore_last_session=" << (prefs_.restore_last_session ? 1 : 0) << "\n";
|
||||
|
||||
// Editor Behavior
|
||||
ss << "backup_before_save=" << (prefs_.backup_before_save ? 1 : 0) << "\n";
|
||||
ss << "default_editor=" << prefs_.default_editor << "\n";
|
||||
|
||||
// Performance
|
||||
ss << "vsync=" << (prefs_.vsync ? 1 : 0) << "\n";
|
||||
ss << "target_fps=" << prefs_.target_fps << "\n";
|
||||
ss << "cache_size_mb=" << prefs_.cache_size_mb << "\n";
|
||||
ss << "undo_history_size=" << prefs_.undo_history_size << "\n";
|
||||
|
||||
// AI Agent
|
||||
ss << "ai_provider=" << prefs_.ai_provider << "\n";
|
||||
ss << "ollama_url=" << prefs_.ollama_url << "\n";
|
||||
ss << "gemini_api_key=" << prefs_.gemini_api_key << "\n";
|
||||
ss << "ai_temperature=" << prefs_.ai_temperature << "\n";
|
||||
ss << "ai_max_tokens=" << prefs_.ai_max_tokens << "\n";
|
||||
ss << "ai_proactive=" << (prefs_.ai_proactive ? 1 : 0) << "\n";
|
||||
ss << "ai_auto_learn=" << (prefs_.ai_auto_learn ? 1 : 0) << "\n";
|
||||
ss << "ai_multimodal=" << (prefs_.ai_multimodal ? 1 : 0) << "\n";
|
||||
|
||||
// CLI Logging
|
||||
ss << "log_level=" << prefs_.log_level << "\n";
|
||||
ss << "log_to_file=" << (prefs_.log_to_file ? 1 : 0) << "\n";
|
||||
ss << "log_file_path=" << prefs_.log_file_path << "\n";
|
||||
ss << "log_ai_requests=" << (prefs_.log_ai_requests ? 1 : 0) << "\n";
|
||||
ss << "log_rom_operations=" << (prefs_.log_rom_operations ? 1 : 0) << "\n";
|
||||
ss << "log_gui_automation=" << (prefs_.log_gui_automation ? 1 : 0) << "\n";
|
||||
ss << "log_proposals=" << (prefs_.log_proposals ? 1 : 0) << "\n";
|
||||
|
||||
util::SaveFile(settings_file_path_, ss.str());
|
||||
} catch (const std::exception& e) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("Failed to save user settings: %s", e.what()));
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace editor {
|
||||
class UserSettings {
|
||||
public:
|
||||
struct Preferences {
|
||||
// General
|
||||
float font_global_scale = 1.0f;
|
||||
bool backup_rom = false;
|
||||
bool save_new_auto = true;
|
||||
@@ -23,6 +24,35 @@ class UserSettings {
|
||||
std::string last_project_path;
|
||||
bool show_welcome_on_startup = true;
|
||||
bool restore_last_session = true;
|
||||
|
||||
// Editor Behavior
|
||||
bool backup_before_save = true;
|
||||
int default_editor = 0; // 0=None, 1=Overworld, 2=Dungeon, 3=Graphics
|
||||
|
||||
// Performance
|
||||
bool vsync = true;
|
||||
int target_fps = 60;
|
||||
int cache_size_mb = 512;
|
||||
int undo_history_size = 50;
|
||||
|
||||
// AI Agent
|
||||
int ai_provider = 0; // 0=Ollama, 1=Gemini, 2=Mock
|
||||
std::string ollama_url = "http://localhost:11434";
|
||||
std::string gemini_api_key;
|
||||
float ai_temperature = 0.7f;
|
||||
int ai_max_tokens = 2048;
|
||||
bool ai_proactive = true;
|
||||
bool ai_auto_learn = true;
|
||||
bool ai_multimodal = true;
|
||||
|
||||
// CLI Logging
|
||||
int log_level = 1; // 0=Debug, 1=Info, 2=Warning, 3=Error, 4=Fatal
|
||||
bool log_to_file = false;
|
||||
std::string log_file_path;
|
||||
bool log_ai_requests = true;
|
||||
bool log_rom_operations = true;
|
||||
bool log_gui_automation = true;
|
||||
bool log_proposals = true;
|
||||
};
|
||||
|
||||
UserSettings();
|
||||
|
||||
76
src/app/editor/ui/menu_manager.cc
Normal file
76
src/app/editor/ui/menu_manager.cc
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "app/editor/ui/menu_manager.h"
|
||||
|
||||
#include "app/editor/editor_manager.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
MenuManager::MenuManager(EditorManager* editor_manager)
|
||||
: editor_manager_(editor_manager) {}
|
||||
|
||||
void MenuManager::BuildAndDraw() {
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
editor_manager_->BuildModernMenu(); // This contains the menu_builder_ logic
|
||||
editor_manager_->menu_builder_.Draw();
|
||||
|
||||
// This is the logic from the second half of DrawMenuBar
|
||||
auto* current_rom = editor_manager_->GetCurrentRom();
|
||||
std::string version_text = absl::StrFormat("v%s", editor_manager_->version().c_str());
|
||||
float version_width = ImGui::CalcTextSize(version_text.c_str()).x;
|
||||
float session_rom_area_width = 280.0f;
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - version_width - 10 - session_rom_area_width);
|
||||
|
||||
if (editor_manager_->GetActiveSessionCount() > 1) {
|
||||
if (ImGui::SmallButton(absl::StrFormat("%s%zu", ICON_MD_TAB, editor_manager_->GetActiveSessionCount()).c_str())) {
|
||||
editor_manager_->show_session_switcher_ = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Sessions: %zu active\nClick to switch", editor_manager_->GetActiveSessionCount());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (current_rom && current_rom->is_loaded()) {
|
||||
if (ImGui::SmallButton(ICON_MD_APPS)) {
|
||||
editor_manager_->show_editor_selection_ = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_DASHBOARD " Editor Selection (Ctrl+E)");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_DISPLAY_SETTINGS)) {
|
||||
editor_manager_->popup_manager_->Show("Display Settings");
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(ICON_MD_TUNE " Display Settings");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Separator();
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
if (current_rom && current_rom->is_loaded()) {
|
||||
std::string rom_display = current_rom->title();
|
||||
if (rom_display.length() > 22) {
|
||||
rom_display = rom_display.substr(0, 19) + "...";
|
||||
}
|
||||
ImVec4 status_color = current_rom->dirty() ? ImVec4(1.0f, 0.5f, 0.0f, 1.0f) : ImVec4(0.0f, 0.8f, 0.0f, 1.0f);
|
||||
if (ImGui::SmallButton(absl::StrFormat("%s%s", rom_display.c_str(), current_rom->dirty() ? "*" : "").c_str())) {
|
||||
ImGui::OpenPopup("ROM Details");
|
||||
}
|
||||
// ... (rest of the popup logic from DrawMenuBar)
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "No ROM");
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - version_width - 10);
|
||||
ImGui::Text("%s", version_text.c_str());
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
25
src/app/editor/ui/menu_manager.h
Normal file
25
src/app/editor/ui/menu_manager.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef YAZE_APP_EDITOR_UI_MENU_MANAGER_H_
|
||||
#define YAZE_APP_EDITOR_UI_MENU_MANAGER_H_
|
||||
|
||||
#include "app/editor/ui/menu_builder.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
class EditorManager;
|
||||
|
||||
class MenuManager {
|
||||
public:
|
||||
explicit MenuManager(EditorManager* editor_manager);
|
||||
|
||||
void BuildAndDraw();
|
||||
|
||||
private:
|
||||
EditorManager* editor_manager_;
|
||||
MenuBuilder menu_builder_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_EDITOR_UI_MENU_MANAGER_H_
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "app/editor/system/toast_manager.h"
|
||||
#include "app/rom.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/platform_paths.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
@@ -31,6 +33,27 @@ absl::Status WorkspaceManager::ResetWorkspaceLayout() {
|
||||
}
|
||||
|
||||
void WorkspaceManager::SaveWorkspacePreset(const std::string& name) {
|
||||
if (name.empty()) return;
|
||||
std::string ini_name = absl::StrFormat("yaze_workspace_%s.ini", name.c_str());
|
||||
ImGui::SaveIniSettingsToDisk(ini_name.c_str());
|
||||
|
||||
if (!workspace_presets_loaded_) {
|
||||
// RefreshWorkspacePresets(); // This will be implemented next
|
||||
}
|
||||
|
||||
if (std::find(workspace_presets_.begin(), workspace_presets_.end(), name) ==
|
||||
workspace_presets_.end()) {
|
||||
workspace_presets_.emplace_back(name);
|
||||
try {
|
||||
std::ostringstream ss;
|
||||
for (const auto& n : workspace_presets_)
|
||||
ss << n << "\n";
|
||||
// This should use a platform-agnostic path
|
||||
util::SaveFile("workspace_presets.txt", ss.str());
|
||||
} catch (const std::exception& e) {
|
||||
// LOG_WARN("WorkspaceManager", "Failed to save presets: %s", e.what());
|
||||
}
|
||||
}
|
||||
last_workspace_preset_ = name;
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show(absl::StrFormat("Preset '%s' saved", name),
|
||||
@@ -39,6 +62,9 @@ void WorkspaceManager::SaveWorkspacePreset(const std::string& name) {
|
||||
}
|
||||
|
||||
void WorkspaceManager::LoadWorkspacePreset(const std::string& name) {
|
||||
if (name.empty()) return;
|
||||
std::string ini_name = absl::StrFormat("yaze_workspace_%s.ini", name.c_str());
|
||||
ImGui::LoadIniSettingsFromDisk(ini_name.c_str());
|
||||
last_workspace_preset_ = name;
|
||||
if (toast_manager_) {
|
||||
toast_manager_->Show(absl::StrFormat("Preset '%s' loaded", name),
|
||||
@@ -46,6 +72,34 @@ void WorkspaceManager::LoadWorkspacePreset(const std::string& name) {
|
||||
}
|
||||
}
|
||||
|
||||
void WorkspaceManager::RefreshPresets() {
|
||||
try {
|
||||
std::vector<std::string> new_presets;
|
||||
auto config_dir = util::PlatformPaths::GetConfigDirectory();
|
||||
if (config_dir.ok()) {
|
||||
std::string presets_path = (*config_dir / "workspace_presets.txt").string();
|
||||
auto data = util::LoadFile(presets_path);
|
||||
if (!data.empty()) {
|
||||
std::istringstream ss(data);
|
||||
std::string name;
|
||||
while (std::getline(ss, name)) {
|
||||
name.erase(0, name.find_first_not_of(" \t\r\n"));
|
||||
name.erase(name.find_last_not_of(" \t\r\n") + 1);
|
||||
if (!name.empty() && name.length() < 256) {
|
||||
new_presets.emplace_back(std::move(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
workspace_presets_ = std::move(new_presets);
|
||||
workspace_presets_loaded_ = true;
|
||||
} catch (const std::exception& e) {
|
||||
// LOG_ERROR("WorkspaceManager", "Error refreshing presets: %s", e.what());
|
||||
workspace_presets_.clear();
|
||||
workspace_presets_loaded_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WorkspaceManager::LoadDeveloperLayout() {
|
||||
// TODO: Load preset with all debug tools
|
||||
if (toast_manager_) {
|
||||
|
||||
@@ -36,6 +36,7 @@ class WorkspaceManager {
|
||||
// Preset management
|
||||
void SaveWorkspacePreset(const std::string& name);
|
||||
void LoadWorkspacePreset(const std::string& name);
|
||||
void RefreshPresets();
|
||||
void LoadDeveloperLayout();
|
||||
void LoadDesignerLayout();
|
||||
void LoadModderLayout();
|
||||
@@ -53,10 +54,15 @@ class WorkspaceManager {
|
||||
|
||||
void set_sessions(std::deque<SessionInfo>* sessions) { sessions_ = sessions; }
|
||||
|
||||
const std::vector<std::string>& workspace_presets() const { return workspace_presets_; }
|
||||
bool workspace_presets_loaded() const { return workspace_presets_loaded_; }
|
||||
|
||||
private:
|
||||
ToastManager* toast_manager_;
|
||||
std::deque<SessionInfo>* sessions_ = nullptr;
|
||||
std::string last_workspace_preset_;
|
||||
std::vector<std::string> workspace_presets_;
|
||||
bool workspace_presets_loaded_ = false;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze::gfx {
|
||||
|
||||
@@ -43,19 +44,18 @@ void BackgroundBuffer::DrawTile(const TileInfo& tile, uint8_t* canvas,
|
||||
// DEBUG: For floor tiles, check what we're actually reading
|
||||
static int debug_count = 0;
|
||||
if (debug_count < 4 && (tile.id_ == 0xEC || tile.id_ == 0xED || tile.id_ == 0xFC || tile.id_ == 0xFD)) {
|
||||
printf("[DrawTile] Floor tile 0x%02X at sheet pos (%d,%d), palette=%d, mirror=(%d,%d)\n",
|
||||
LOG_DEBUG("[DrawTile]", "Floor tile 0x%02X at sheet pos (%d,%d), palette=%d, mirror=(%d,%d)",
|
||||
tile.id_, tile_x, tile_y, tile.palette_, tile.horizontal_mirror_, tile.vertical_mirror_);
|
||||
printf("[DrawTile] First row (8 pixels): ");
|
||||
LOG_DEBUG("[DrawTile]", "First row (8 pixels): ");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int src_index = tile_y * 128 + (tile_x + i);
|
||||
printf("%d ", tiledata[src_index]);
|
||||
LOG_DEBUG("[DrawTile]", "%d ", tiledata[src_index]);
|
||||
}
|
||||
printf("\n[DrawTile] Second row (8 pixels): ");
|
||||
LOG_DEBUG("[DrawTile]", "Second row (8 pixels): ");
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int src_index = (tile_y + 1) * 128 + (tile_x + i);
|
||||
printf("%d ", tiledata[src_index]);
|
||||
LOG_DEBUG("[DrawTile]", "%d ", tiledata[src_index]);
|
||||
}
|
||||
printf("\n");
|
||||
debug_count++;
|
||||
}
|
||||
|
||||
@@ -146,13 +146,13 @@ void BackgroundBuffer::DrawFloor(const std::vector<uint8_t>& rom_data,
|
||||
uint8_t floor_graphics) {
|
||||
// Create bitmap ONCE at the start if it doesn't exist
|
||||
if (!bitmap_.is_active() || bitmap_.width() == 0) {
|
||||
printf("[DrawFloor] Creating bitmap: %dx%d, active=%d, width=%d\n",
|
||||
LOG_DEBUG("[DrawFloor]", "Creating bitmap: %dx%d, active=%d, width=%d",
|
||||
width_, height_, bitmap_.is_active(), bitmap_.width());
|
||||
bitmap_.Create(width_, height_, 8, std::vector<uint8_t>(width_ * height_, 0));
|
||||
printf("[DrawFloor] After Create: active=%d, width=%d, height=%d\n",
|
||||
LOG_DEBUG("[DrawFloor]", "After Create: active=%d, width=%d, height=%d",
|
||||
bitmap_.is_active(), bitmap_.width(), bitmap_.height());
|
||||
} else {
|
||||
printf("[DrawFloor] Bitmap already exists: active=%d, width=%d, height=%d\n",
|
||||
LOG_DEBUG("[DrawFloor]", "Bitmap already exists: active=%d, width=%d, height=%d",
|
||||
bitmap_.is_active(), bitmap_.width(), bitmap_.height());
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,16 @@ using namespace yaze;
|
||||
DEFINE_FLAG(std::string, rom_file, "", "The ROM file to load.");
|
||||
DEFINE_FLAG(std::string, log_file, "", "Output log file path for debugging.");
|
||||
DEFINE_FLAG(bool, debug, false, "Enable debug logging and verbose output.");
|
||||
DEFINE_FLAG(std::string, editor, "",
|
||||
"The editor to open on startup. "
|
||||
"Available editors: Assembly, Dungeon, Graphics, Music, Overworld, "
|
||||
"Palette, Screen, Sprite, Message, Hex, Agent, Settings. "
|
||||
"Example: --editor=Dungeon");
|
||||
DEFINE_FLAG(std::string, cards, "",
|
||||
"A comma-separated list of cards to open within the specified editor. "
|
||||
"For Dungeon editor: 'Rooms List', 'Room Matrix', 'Entrances List', "
|
||||
"'Room Graphics', 'Object Editor', 'Palette Editor', or 'Room N' (where N is room ID). "
|
||||
"Example: --cards=\"Rooms List,Room 0,Room 105\"");
|
||||
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
// gRPC test harness flags
|
||||
@@ -102,6 +112,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
auto controller = std::make_unique<core::Controller>();
|
||||
EXIT_IF_ERROR(controller->OnEntry(rom_filename))
|
||||
|
||||
// Set startup editor and cards from flags (after OnEntry initializes editor manager)
|
||||
if (!FLAGS_editor->Get().empty()) {
|
||||
controller->SetStartupEditor(FLAGS_editor->Get(), FLAGS_cards->Get());
|
||||
}
|
||||
|
||||
while (controller->IsActive()) {
|
||||
controller->OnInput();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
@@ -26,27 +27,27 @@ absl::Status ObjectDrawer::DrawObject(const RoomObject& object,
|
||||
|
||||
// Ensure object has tiles loaded
|
||||
auto mutable_obj = const_cast<RoomObject&>(object);
|
||||
printf("[DrawObject] Setting ROM for object ID=0x%02X\n", object.id_);
|
||||
LOG_DEBUG("ObjectDrawer", "Setting ROM for object ID=0x%02X", object.id_);
|
||||
mutable_obj.set_rom(rom_);
|
||||
printf("[DrawObject] Calling EnsureTilesLoaded for object ID=0x%02X\n", object.id_);
|
||||
LOG_DEBUG("ObjectDrawer", "Calling EnsureTilesLoaded for object ID=0x%02X", object.id_);
|
||||
mutable_obj.EnsureTilesLoaded();
|
||||
|
||||
// Check if tiles were actually loaded on the mutable object
|
||||
printf("[DrawObject] After EnsureTilesLoaded: mutable object has %zu tiles\n",
|
||||
LOG_DEBUG("ObjectDrawer", "After EnsureTilesLoaded: mutable object has %zu tiles",
|
||||
mutable_obj.tiles().size());
|
||||
|
||||
// Select buffer based on layer
|
||||
auto& target_bg = (object.layer_ == RoomObject::LayerType::BG2) ? bg2 : bg1;
|
||||
printf("[DrawObject] Object ID=0x%02X using %s buffer (layer=%d)\n",
|
||||
LOG_DEBUG("ObjectDrawer", "Object ID=0x%02X using %s buffer (layer=%d)",
|
||||
object.id_, (object.layer_ == RoomObject::LayerType::BG2) ? "BG2" : "BG1", object.layer_);
|
||||
|
||||
// Skip objects that don't have tiles loaded - check mutable object
|
||||
if (mutable_obj.tiles().empty()) {
|
||||
printf("[DrawObject] Object ID=0x%02X has no tiles loaded, skipping\n", object.id_);
|
||||
LOG_DEBUG("ObjectDrawer", "Object ID=0x%02X has no tiles loaded, skipping", object.id_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
printf("[DrawObject] Object ID=0x%02X has %zu tiles, proceeding with drawing\n",
|
||||
LOG_DEBUG("ObjectDrawer", "Object ID=0x%02X has %zu tiles, proceeding with drawing",
|
||||
object.id_, mutable_obj.tiles().size());
|
||||
|
||||
// Look up draw routine for this object
|
||||
@@ -70,7 +71,7 @@ absl::Status ObjectDrawer::DrawObjectList(
|
||||
gfx::BackgroundBuffer& bg2,
|
||||
const gfx::PaletteGroup& palette_group) {
|
||||
|
||||
printf("[DrawObjectList] Drawing %zu objects\n", objects.size());
|
||||
LOG_DEBUG("ObjectDrawer", "Drawing %zu objects", objects.size());
|
||||
|
||||
int drawn_count = 0;
|
||||
int skipped_count = 0;
|
||||
@@ -90,7 +91,7 @@ absl::Status ObjectDrawer::DrawObjectList(
|
||||
|
||||
// Only log if there are failures
|
||||
if (skipped_count > 0) {
|
||||
printf("[ObjectDrawer] Drew %d objects, skipped %d\n", drawn_count, skipped_count);
|
||||
LOG_DEBUG("ObjectDrawer", "Drew %d objects, skipped %d", drawn_count, skipped_count);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -575,14 +576,14 @@ void ObjectDrawer::DrawRightwardsDecor2x2spaced12_1to16(const RoomObject& obj, g
|
||||
|
||||
void ObjectDrawer::WriteTile16(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
|
||||
const gfx::Tile16& tile) {
|
||||
printf("[WriteTile16] Writing Tile16 at tile pos (%d,%d) to bitmap\n", tile_x, tile_y);
|
||||
LOG_DEBUG("ObjectDrawer", "Writing Tile16 at tile pos (%d,%d) to bitmap", tile_x, tile_y);
|
||||
|
||||
// Draw directly to bitmap instead of tile buffer to avoid being overwritten
|
||||
auto& bitmap = bg.bitmap();
|
||||
printf("[WriteTile16] Bitmap status: active=%d, width=%d, height=%d, surface=%p\n",
|
||||
LOG_DEBUG("ObjectDrawer", "Bitmap status: active=%d, width=%d, height=%d, surface=%p",
|
||||
bitmap.is_active(), bitmap.width(), bitmap.height(), bitmap.surface());
|
||||
if (!bitmap.is_active() || bitmap.width() == 0) {
|
||||
printf("[WriteTile16] Bitmap not ready: active=%d, width=%d\n", bitmap.is_active(), bitmap.width());
|
||||
LOG_DEBUG("ObjectDrawer", "Bitmap not ready: active=%d, width=%d", bitmap.is_active(), bitmap.width());
|
||||
return; // Bitmap not ready
|
||||
}
|
||||
|
||||
@@ -615,7 +616,7 @@ void ObjectDrawer::DrawTileToBitmap(gfx::Bitmap& bitmap, const gfx::TileInfo& ti
|
||||
|
||||
// DEBUG: Check if bitmap is valid
|
||||
if (!bitmap.is_active() || bitmap.width() == 0 || bitmap.height() == 0) {
|
||||
printf("[DrawTileToBitmap] ERROR: Invalid bitmap - active=%d, size=%dx%d\n",
|
||||
LOG_DEBUG("ObjectDrawer", "ERROR: Invalid bitmap - active=%d, size=%dx%d",
|
||||
bitmap.is_active(), bitmap.width(), bitmap.height());
|
||||
return;
|
||||
}
|
||||
@@ -658,7 +659,7 @@ void ObjectDrawer::DrawTileToBitmap(gfx::Bitmap& bitmap, const gfx::TileInfo& ti
|
||||
|
||||
// Debug first pixel of each tile
|
||||
if (py == 0 && px == 0) {
|
||||
printf("[DrawTileToBitmap] Tile ID=0x%02X at (%d,%d): palette=%d, pixel_index=%d, final_color=%d\n",
|
||||
LOG_DEBUG("ObjectDrawer", "Tile ID=0x%02X at (%d,%d): palette=%d, pixel_index=%d, final_color=%d",
|
||||
tile_info.id_, pixel_x, pixel_y, palette_id, pixel_index, final_color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "app/zelda3/dungeon/room_diagnostic.h"
|
||||
#include "app/zelda3/dungeon/room_object.h"
|
||||
#include "app/zelda3/sprite/sprite.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
@@ -312,7 +313,7 @@ void Room::RenderRoomGraphics() {
|
||||
int palette_id = palette;
|
||||
|
||||
if (palette_id < 0 || palette_id >= num_palettes) {
|
||||
std::printf("5. WARNING: palette_id %d is out of bounds [0, %d), using palette %d\n",
|
||||
LOG_DEBUG("[RenderRoomGraphics]", "5. WARNING: palette_id %d is out of bounds [0, %d), using palette %d",
|
||||
palette_id, num_palettes, palette_id % num_palettes);
|
||||
palette_id = palette_id % num_palettes; // Use modulo to wrap around instead of defaulting to 0
|
||||
}
|
||||
@@ -334,10 +335,10 @@ void Room::RenderRoomGraphics() {
|
||||
}
|
||||
|
||||
void Room::RenderObjectsToBackground() {
|
||||
printf("[RenderObjectsToBackground] Starting object rendering\n");
|
||||
LOG_DEBUG("[RenderObjectsToBackground]", "Starting object rendering");
|
||||
|
||||
if (!rom_ || !rom_->is_loaded()) {
|
||||
printf("[RenderObjectsToBackground] ROM not loaded, aborting\n");
|
||||
LOG_DEBUG("[RenderObjectsToBackground]", "ROM not loaded, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -368,7 +369,7 @@ void Room::RenderObjectsToBackground() {
|
||||
|
||||
// Log only failures, not successes
|
||||
if (!status.ok()) {
|
||||
printf("[RenderObjectsToBackground] ObjectDrawer failed: %s\n", std::string(status.message()).c_str());
|
||||
LOG_DEBUG("[RenderObjectsToBackground]", "ObjectDrawer failed: %s", std::string(status.message()).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/zelda3/dungeon/object_parser.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
@@ -153,28 +154,28 @@ void RoomObject::DrawTile(gfx::Tile16 t, int xx, int yy,
|
||||
}
|
||||
|
||||
void RoomObject::EnsureTilesLoaded() {
|
||||
printf("[EnsureTilesLoaded] Object ID=0x%02X, tiles_loaded=%d\n", id_, tiles_loaded_);
|
||||
LOG_DEBUG("RoomObject", "Object ID=0x%02X, tiles_loaded=%d", id_, tiles_loaded_);
|
||||
|
||||
if (tiles_loaded_) {
|
||||
printf("[EnsureTilesLoaded] Tiles already loaded for object 0x%02X\n", id_);
|
||||
LOG_DEBUG("RoomObject", "Tiles already loaded for object 0x%02X", id_);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rom_ == nullptr) {
|
||||
printf("[EnsureTilesLoaded] ERROR: ROM not set for object 0x%02X\n", id_);
|
||||
LOG_DEBUG("RoomObject", "ERROR: ROM not set for object 0x%02X", id_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try the new parser first - this is more efficient and accurate
|
||||
printf("[EnsureTilesLoaded] Trying parser for object 0x%02X\n", id_);
|
||||
LOG_DEBUG("RoomObject", "Trying parser for object 0x%02X", id_);
|
||||
auto parser_status = LoadTilesWithParser();
|
||||
if (parser_status.ok()) {
|
||||
printf("[EnsureTilesLoaded] Parser succeeded for object 0x%02X, loaded %zu tiles\n", id_, tiles_.size());
|
||||
LOG_DEBUG("RoomObject", "Parser succeeded for object 0x%02X, loaded %zu tiles", id_, tiles_.size());
|
||||
tiles_loaded_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
printf("[EnsureTilesLoaded] Parser failed for object 0x%02X: %s\n", id_, parser_status.message().data());
|
||||
LOG_DEBUG("RoomObject", "Parser failed for object 0x%02X: %s", id_, parser_status.message().data());
|
||||
|
||||
// Fallback to legacy method for compatibility with enhanced validation
|
||||
auto rom_data = rom_->data();
|
||||
|
||||
Reference in New Issue
Block a user