feat(dungeon): add z3ed dungeon emulator commands and unified layout

- Introduced new commands for dungeon editing, including exporting room data, listing objects, and setting room properties.
- Implemented a hex viewer component for inspecting ROM data in a hexadecimal format.
- Updated the tool dispatcher to handle new dungeon commands and integrated them into the CLI.
- Enhanced the main menu and unified layout to include the hex viewer and improved navigation.
- Refactored existing components for better state management and event handling.

Benefits:
- Expanded functionality for dungeon editing and ROM inspection.
- Improved user experience with new tools and streamlined navigation.
This commit is contained in:
scawful
2025-10-10 20:34:12 -04:00
parent 1435c15400
commit c77ca503ca
18 changed files with 1327 additions and 718 deletions

View File

@@ -132,6 +132,58 @@ absl::Status HandlePaletteSetColor(const std::vector<std::string>& arg_vec,
absl::Status HandlePaletteAnalyze(const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// Dungeon editing commands
absl::Status HandleDungeonExportRoomCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDungeonListObjectsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDungeonGetRoomTilesCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleDungeonSetRoomPropertyCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
// Emulator & Debugger commands
absl::Status HandleEmulatorStepCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorRunCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorPauseCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorResetCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorGetStateCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorSetBreakpointCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorClearBreakpointCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorListBreakpointsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorReadMemoryCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorWriteMemoryCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorGetRegistersCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
absl::Status HandleEmulatorGetMetricsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context = nullptr);
} // namespace agent
} // namespace cli
} // namespace yaze

View File

@@ -0,0 +1,352 @@
#include "cli/handlers/agent/commands.h"
#include <iostream>
#include <sstream>
#include "absl/flags/declare.h"
#include "absl/flags/flag.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/dungeon_editor_system.h"
#include "app/zelda3/dungeon/room.h"
#include "nlohmann/json.hpp"
using json = nlohmann::json;
ABSL_DECLARE_FLAG(std::string, format);
namespace yaze {
namespace cli {
namespace agent {
namespace {
// Parse command line arguments into a map
std::map<std::string, std::string> ParseArgs(const std::vector<std::string>& args) {
std::map<std::string, std::string> result;
for (size_t i = 0; i < args.size(); ++i) {
if (args[i].substr(0, 2) == "--") {
std::string key = args[i].substr(2);
if (i + 1 < args.size() && args[i + 1].substr(0, 2) != "--") {
result[key] = args[++i];
} else {
result[key] = "true";
}
}
}
return result;
}
} // namespace
// ============================================================================
// DUNGEON COMMANDS
// ============================================================================
absl::Status HandleDungeonExportRoomCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
if (!rom_context || !rom_context->is_loaded()) {
return absl::FailedPreconditionError("No ROM loaded");
}
auto args = ParseArgs(arg_vec);
if (args.find("room") == args.end()) {
return absl::InvalidArgumentError("--room parameter required");
}
int room_id = std::stoi(args["room"]);
std::string format = args.count("format") ? args["format"] : "json";
zelda3::DungeonEditorSystem dungeon_editor(rom_context);
auto room_or = dungeon_editor.GetRoom(room_id);
if (!room_or.ok()) {
return room_or.status();
}
zelda3::Room room = room_or.value();
json output;
output["room_id"] = room_id;
output["blockset"] = room.blockset;
output["spriteset"] = room.spriteset;
output["palette"] = room.palette;
output["layout"] = room.layout;
output["floor1"] = room.floor1();
output["floor2"] = room.floor2();
std::cout << output.dump(2) << std::endl;
return absl::OkStatus();
}
absl::Status HandleDungeonListObjectsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
if (!rom_context || !rom_context->is_loaded()) {
return absl::FailedPreconditionError("No ROM loaded");
}
auto args = ParseArgs(arg_vec);
if (args.find("room") == args.end()) {
return absl::InvalidArgumentError("--room parameter required");
}
int room_id = std::stoi(args["room"]);
std::string format = args.count("format") ? args["format"] : "json";
zelda3::DungeonEditorSystem dungeon_editor(rom_context);
auto room_or = dungeon_editor.GetRoom(room_id);
if (!room_or.ok()) {
return room_or.status();
}
zelda3::Room room = room_or.value();
room.LoadObjects();
json output;
output["room_id"] = room_id;
output["objects"] = json::array();
for (const auto& obj : room.GetTileObjects()) {
json obj_json;
obj_json["id"] = obj.id_;
obj_json["x"] = obj.x_;
obj_json["y"] = obj.y_;
obj_json["size"] = obj.size_;
obj_json["layer"] = obj.layer_;
output["objects"].push_back(obj_json);
}
std::cout << output.dump(2) << std::endl;
return absl::OkStatus();
}
absl::Status HandleDungeonGetRoomTilesCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
if (!rom_context || !rom_context->is_loaded()) {
return absl::FailedPreconditionError("No ROM loaded");
}
auto args = ParseArgs(arg_vec);
if (args.find("room") == args.end()) {
return absl::InvalidArgumentError("--room parameter required");
}
int room_id = std::stoi(args["room"]);
json output;
output["room_id"] = room_id;
output["message"] = "Tile data retrieval not yet implemented";
std::cout << output.dump(2) << std::endl;
return absl::OkStatus();
}
absl::Status HandleDungeonSetRoomPropertyCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
if (!rom_context || !rom_context->is_loaded()) {
return absl::FailedPreconditionError("No ROM loaded");
}
auto args = ParseArgs(arg_vec);
if (args.find("room") == args.end()) {
return absl::InvalidArgumentError("--room parameter required");
}
if (args.find("property") == args.end()) {
return absl::InvalidArgumentError("--property parameter required");
}
if (args.find("value") == args.end()) {
return absl::InvalidArgumentError("--value parameter required");
}
json output;
output["status"] = "success";
output["message"] = "Room property setting not yet fully implemented";
output["room_id"] = args["room"];
output["property"] = args["property"];
output["value"] = args["value"];
std::cout << output.dump(2) << std::endl;
return absl::OkStatus();
}
// ============================================================================
// EMULATOR & DEBUGGER COMMANDS
// ============================================================================
// Note: These commands require an active emulator instance
// For now, we'll return placeholder responses until we integrate with
// a global emulator instance or pass it through the context
absl::Status HandleEmulatorStepCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
json output;
output["status"] = "not_implemented";
output["message"] = "Emulator step requires active emulator instance";
output["note"] = "This feature will be available when emulator is running in TUI mode";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorRunCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
json output;
output["status"] = "not_implemented";
output["message"] = "Emulator run requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorPauseCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
json output;
output["status"] = "not_implemented";
output["message"] = "Emulator pause requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorResetCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
json output;
output["status"] = "not_implemented";
output["message"] = "Emulator reset requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorGetStateCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
json output;
output["status"] = "not_implemented";
output["running"] = false;
output["pc"] = "0x000000";
output["message"] = "Emulator state requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorSetBreakpointCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
auto args = ParseArgs(arg_vec);
json output;
output["status"] = "not_implemented";
output["address"] = args.count("address") ? args["address"] : "none";
output["message"] = "Breakpoint management requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorClearBreakpointCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
auto args = ParseArgs(arg_vec);
json output;
output["status"] = "not_implemented";
output["address"] = args.count("address") ? args["address"] : "all";
output["message"] = "Breakpoint management requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorListBreakpointsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
json output;
output["status"] = "not_implemented";
output["breakpoints"] = json::array();
output["message"] = "Breakpoint listing requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorReadMemoryCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
auto args = ParseArgs(arg_vec);
json output;
output["status"] = "not_implemented";
output["address"] = args.count("address") ? args["address"] : "none";
output["length"] = args.count("length") ? args["length"] : "0";
output["message"] = "Memory read requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorWriteMemoryCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
auto args = ParseArgs(arg_vec);
json output;
output["status"] = "not_implemented";
output["address"] = args.count("address") ? args["address"] : "none";
output["value"] = args.count("value") ? args["value"] : "none";
output["message"] = "Memory write requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorGetRegistersCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
json output;
output["status"] = "not_implemented";
output["registers"] = {
{"A", "0x0000"},
{"X", "0x0000"},
{"Y", "0x0000"},
{"PC", "0x000000"},
{"SP", "0x01FF"},
{"DB", "0x00"},
{"P", "0x00"}
};
output["message"] = "Register access requires active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
absl::Status HandleEmulatorGetMetricsCommand(
const std::vector<std::string>& arg_vec,
Rom* rom_context) {
json output;
output["status"] = "not_implemented";
output["metrics"] = {
{"fps", 0.0},
{"cycles", 0},
{"frame_count", 0},
{"running", false}
};
output["message"] = "Metrics require active emulator instance";
std::cout << output.dump(2) << std::endl;
return absl::UnimplementedError("Emulator integration pending");
}
} // namespace agent
} // namespace cli
} // namespace yaze