feat: Update README and z3ed documentation for AI-powered features and editor integration

refactor: Simplify dungeon editor integration tests by using real ROM data and removing mock implementations
This commit is contained in:
scawful
2025-10-04 13:53:34 -04:00
parent c39b1e5a51
commit dc3a59f03b
4 changed files with 44 additions and 457 deletions

View File

@@ -7,11 +7,11 @@ A modern, cross-platform editor for The Legend of Zelda: A Link to the Past ROM
## Version 0.3.2 - Release
#### Asar 65816 Assembler Integration
- **Cross-platform ROM patching** with assembly code support
- **Symbol extraction** with addresses and opcodes from assembly files
- **Assembly validation** with comprehensive error reporting
- **Modern C++ API** with safe memory management
#### z3ed agent - AI-powered CLI assistant
- **AI-assisted ROM hacking** with ollama and Gemini support
- **Natural language commands** for editing and querying ROM data
- **Tool calling** for structured data extraction and modification
- **Interactive chat** with conversation history and context
#### ZSCustomOverworld v3
- **Enhanced overworld editing** capabilities
@@ -19,6 +19,12 @@ A modern, cross-platform editor for The Legend of Zelda: A Link to the Past ROM
- **Custom graphics support** and tile management
- **Improved compatibility** with existing projects
#### Asar 65816 Assembler Integration
- **Cross-platform ROM patching** with assembly code support
- **Symbol extraction** with addresses and opcodes from assembly files
- **Assembly validation** with comprehensive error reporting
- **Modern C++ API** with safe memory management
#### Advanced Features
- **Theme Management**: Complete theme system with 5+ built-in themes and custom theme editor
- **Multi-Session Support**: Work with multiple ROMs simultaneously in docked workspace
@@ -28,13 +34,6 @@ A modern, cross-platform editor for The Legend of Zelda: A Link to the Past ROM
- **Modern CLI**: Enhanced z3ed tool with interactive TUI and subcommands
- **Cross-Platform**: Full support for Windows, macOS, and Linux
### 🛠️ Technical Improvements
- **Modern CMake 3.16+**: Target-based configuration and build system
- **CMakePresets**: Development workflow presets for better productivity
- **Cross-platform CI/CD**: Automated builds and testing for all platforms
- **Professional packaging**: NSIS, DMG, and DEB/RPM installers
- **Enhanced testing**: ROM-dependent test separation for CI compatibility
## Quick Start
### Build

View File

@@ -11,7 +11,7 @@ This document is the **source of truth** for the z3ed CLI architecture, design,
### Core Capabilities
1. **Conversational Agent**: Chat with an AI (Ollama or Gemini) to explore ROM contents and plan changes using natural language.
1. **Conversational Agent**: Chat with an AI (Ollama or Gemini) to explore ROM contents and plan changes using natural language—available from the CLI, terminal UI, and now directly within the YAZE editor.
2. **GUI Test Automation**: A gRPC-based test harness allows for widget discovery, test recording/replay, and introspection for debugging and AI-driven validation.
3. **Proposal System**: A safe, sandboxed editing workflow where all changes are tracked as "proposals" that require human review and acceptance.
4. **Resource-Oriented CLI**: A clean `z3ed <resource> <action>` command structure that is both human-readable and machine-parsable.
@@ -150,8 +150,8 @@ Full-screen interactive terminal with table rendering, syntax highlighting, and
### Simple Chat (`agent simple-chat`)
Lightweight, scriptable text-based REPL that supports single messages, interactive sessions, piped input, and batch files.
### GUI Chat Widget (In Progress)
An ImGui widget in the main YAZE editor that will provide the same functionality with a graphical interface.
### GUI Chat Widget (Editor Integration Preview)
Accessible from **Debug → Agent Chat** inside YAZE. Provides the same conversation loop as the CLI, including streaming history, JSON/table inspection, and ROM-aware tool dispatch. Current limitations: no proposal preview shortcuts yet, and the window state resets on restart.
## 7. AI Provider Configuration
@@ -170,13 +170,13 @@ Z3ED supports multiple AI providers. Configuration is resolved with command-line
- **Core Infrastructure**: Resource-oriented CLI, proposal workflow, sandbox manager, and resource catalog are all production-ready.
- **AI Backends**: Both Ollama (local) and Gemini (cloud) are operational.
- **Conversational Agent**: The agent service, tool dispatcher (with 5 read-only tools), and TUI/simple chat interfaces are complete.
- **Conversational Agent**: The agent service, tool dispatcher (with 5 read-only tools), TUI/simple chat interfaces, and initial ImGui editor chat widget are complete.
- **GUI Test Harness**: A comprehensive GUI testing platform with introspection, widget discovery, recording/replay, and CI integration support.
### 🚧 Active & Next Steps
1. **Live LLM Testing (1-2h)**: Verify function calling with real models (Ollama/Gemini).
2. **GUI Chat Integration (6-8h)**: Wire the `AgentChatWidget` into the main YAZE editor.
2. **GUI Chat Enhancements (4-6h)**: Persist chat state, surface proposal shortcuts, and add toast notifications when new proposals arrive from chats.
3. **Expand Tool Coverage (8-10h)**: Add new read-only tools for inspecting dialogue, sprites, and regions.
4. **Windows Cross-Platform Testing (8-10h)**: Validate `z3ed` and the test harness on Windows.

View File

@@ -1,10 +1,5 @@
#include "integration/dungeon_editor_test.h"
#include <cstring>
#include <vector>
#include "absl/strings/str_format.h"
#include "app/snes.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/room_object.h"
@@ -13,382 +8,26 @@ namespace test {
using namespace yaze::zelda3;
void DungeonEditorIntegrationTest::SetUp() {
ASSERT_TRUE(CreateMockRom().ok());
ASSERT_TRUE(LoadTestRoomData().ok());
dungeon_editor_ = std::make_unique<editor::DungeonEditor>(mock_rom_.get());
dungeon_editor_->Initialize();
}
void DungeonEditorIntegrationTest::TearDown() {
dungeon_editor_.reset();
mock_rom_.reset();
}
absl::Status DungeonEditorIntegrationTest::CreateMockRom() {
mock_rom_ = std::make_unique<MockRom>();
// Generate mock ROM data
std::vector<uint8_t> mock_data(kMockRomSize, 0x00);
// Set up basic ROM structure
// Header at 0x7FC0
std::string title = "ZELDA3 TEST ROM";
std::memcpy(&mock_data[0x7FC0], title.c_str(), std::min(title.length(), size_t(21)));
// Set ROM size and type
mock_data[0x7FD7] = 0x21; // 2MB ROM
mock_data[0x7FD8] = 0x00; // SRAM size
mock_data[0x7FD9] = 0x00; // Country code (NTSC)
mock_data[0x7FDA] = 0x00; // License code
mock_data[0x7FDB] = 0x00; // Version
// Set up room header pointers
mock_data[0xB5DD] = 0x00; // Room header pointer low
mock_data[0xB5DE] = 0x00; // Room header pointer mid
mock_data[0xB5DF] = 0x00; // Room header pointer high
// Set up object pointers
mock_data[0x874C] = 0x00; // Object pointer low
mock_data[0x874D] = 0x00; // Object pointer mid
mock_data[0x874E] = 0x00; // Object pointer high
return mock_rom_->LoadAndOwnData(mock_data);
}
absl::Status DungeonEditorIntegrationTest::LoadTestRoomData() {
// Generate test room data
auto room_header = GenerateMockRoomHeader(kTestRoomId);
auto object_data = GenerateMockObjectData();
auto graphics_data = GenerateMockGraphicsData();
auto mock_rom = static_cast<MockRom*>(mock_rom_.get());
mock_rom->SetMockRoomData(kTestRoomId, room_header);
mock_rom->SetMockObjectData(kTestObjectId, object_data);
mock_rom->SetMockGraphicsData(graphics_data);
return absl::OkStatus();
}
absl::Status DungeonEditorIntegrationTest::TestObjectParsing() {
// Test object parsing without SNES emulation
auto room = zelda3::LoadRoomFromRom(mock_rom_.get(), kTestRoomId);
// Verify room was loaded correctly
// Test cases using real ROM
TEST_F(DungeonEditorIntegrationTest, LoadRoomFromRealRom) {
auto room = zelda3::LoadRoomFromRom(rom_.get(), kTestRoomId);
EXPECT_NE(room.rom(), nullptr);
// Note: room_id_ is private, so we can't directly access it in tests
// Test object loading
room.LoadObjects();
EXPECT_FALSE(room.GetTileObjects().empty());
// Verify object properties
for (const auto& obj : room.GetTileObjects()) {
// Note: id_ is private, so we can't directly access it in tests
EXPECT_LE(obj.x_, 31); // Room width limit
EXPECT_LE(obj.y_, 31); // Room height limit
// Note: rom() method is not const, so we can't call it on const objects
}
return absl::OkStatus();
}
absl::Status DungeonEditorIntegrationTest::TestObjectRendering() {
// Test object rendering without SNES emulation
auto room = zelda3::LoadRoomFromRom(mock_rom_.get(), kTestRoomId);
TEST_F(DungeonEditorIntegrationTest, DungeonEditorInitialization) {
ASSERT_TRUE(dungeon_editor_->Load().ok());
}
TEST_F(DungeonEditorIntegrationTest, ObjectEncodingRoundTrip) {
auto room = zelda3::LoadRoomFromRom(rom_.get(), kTestRoomId);
room.LoadObjects();
// Test tile loading for objects
for (auto& obj : room.GetTileObjects()) {
obj.EnsureTilesLoaded();
EXPECT_FALSE(obj.tiles_.empty());
}
// Test room graphics rendering
room.LoadRoomGraphics();
room.RenderRoomGraphics();
return absl::OkStatus();
}
absl::Status DungeonEditorIntegrationTest::TestRoomGraphics() {
// Test room graphics loading and rendering
auto room = zelda3::LoadRoomFromRom(mock_rom_.get(), kTestRoomId);
// Test graphics loading
room.LoadRoomGraphics();
EXPECT_FALSE(room.blocks().empty());
// Test graphics rendering
room.RenderRoomGraphics();
return absl::OkStatus();
}
absl::Status DungeonEditorIntegrationTest::TestPaletteHandling() {
// Test palette loading and application
auto room = zelda3::LoadRoomFromRom(mock_rom_.get(), kTestRoomId);
// Verify palette is set
EXPECT_GE(room.palette, 0);
EXPECT_LE(room.palette, 0x47); // Max palette index
return absl::OkStatus();
}
std::vector<uint8_t> DungeonEditorIntegrationTest::GenerateMockRoomHeader(int room_id) {
std::vector<uint8_t> header(32, 0x00);
// Basic room properties
header[0] = 0x00; // Background type, collision, light
header[1] = 0x00; // Palette
header[2] = 0x01; // Blockset
header[3] = 0x01; // Spriteset
header[4] = 0x00; // Effect
header[5] = 0x00; // Tag1
header[6] = 0x00; // Tag2
header[7] = 0x00; // Staircase planes
header[8] = 0x00; // Staircase planes continued
header[9] = 0x00; // Hole warp
header[10] = 0x00; // Staircase rooms
header[11] = 0x00;
header[12] = 0x00;
header[13] = 0x00;
return header;
}
std::vector<uint8_t> DungeonEditorIntegrationTest::GenerateMockObjectData() {
std::vector<uint8_t> data;
// Add a simple wall object
data.push_back(0x08); // X position (2 tiles)
data.push_back(0x08); // Y position (2 tiles)
data.push_back(0x01); // Object ID (wall)
// Add layer separator
data.push_back(0xFF);
data.push_back(0xFF);
// Add door section
data.push_back(0xF0);
data.push_back(0xFF);
return data;
}
std::vector<uint8_t> DungeonEditorIntegrationTest::GenerateMockGraphicsData() {
std::vector<uint8_t> data(0x4000, 0x00);
// Generate basic tile data
for (size_t i = 0; i < data.size(); i += 2) {
data[i] = 0x00; // Tile low byte
data[i + 1] = 0x00; // Tile high byte
}
return data;
}
absl::Status MockRom::SetMockData(const std::vector<uint8_t>& data) {
backing_buffer_.assign(data.begin(), data.end());
Expand(static_cast<int>(backing_buffer_.size()));
if (!backing_buffer_.empty()) {
std::memcpy(mutable_data(), backing_buffer_.data(), backing_buffer_.size());
}
ClearDirty();
InitializeMemoryLayout();
return absl::OkStatus();
}
absl::Status MockRom::LoadAndOwnData(const std::vector<uint8_t>& data) {
backing_buffer_.assign(data.begin(), data.end());
Expand(static_cast<int>(backing_buffer_.size()));
if (!backing_buffer_.empty()) {
std::memcpy(mutable_data(), backing_buffer_.data(), backing_buffer_.size());
}
ClearDirty();
// Minimal metadata setup via public API
set_filename("mock_rom.sfc");
auto& palette_groups = *mutable_palette_group();
palette_groups.clear();
if (palette_groups.dungeon_main.size() == 0) {
gfx::SnesPalette default_palette;
default_palette.Resize(16);
palette_groups.dungeon_main.AddPalette(default_palette);
}
// Ensure graphics buffer is sized
auto* gfx_buffer = mutable_graphics_buffer();
gfx_buffer->assign(backing_buffer_.begin(), backing_buffer_.end());
InitializeMemoryLayout();
return absl::OkStatus();
}
void MockRom::SetMockRoomData(int room_id, const std::vector<uint8_t>& data) {
mock_room_data_[room_id] = data;
if (room_header_table_pc_ == 0 || room_header_data_base_pc_ == 0) {
return;
}
uint32_t header_offset = room_header_data_base_pc_ + kRoomHeaderStride * static_cast<uint32_t>(room_id);
EnsureBufferCapacity(header_offset + static_cast<uint32_t>(data.size()));
std::memcpy(backing_buffer_.data() + header_offset, data.data(), data.size());
std::memcpy(mutable_data() + header_offset, data.data(), data.size());
uint32_t snes_offset = PcToSnes(header_offset);
uint32_t pointer_entry = room_header_table_pc_ + static_cast<uint32_t>(room_id) * 2;
EnsureBufferCapacity(pointer_entry + 2);
backing_buffer_[pointer_entry] = static_cast<uint8_t>(snes_offset & 0xFF);
backing_buffer_[pointer_entry + 1] = static_cast<uint8_t>((snes_offset >> 8) & 0xFF);
mutable_data()[pointer_entry] = backing_buffer_[pointer_entry];
mutable_data()[pointer_entry + 1] = backing_buffer_[pointer_entry + 1];
}
void MockRom::SetMockObjectData(int object_id, const std::vector<uint8_t>& data) {
mock_object_data_[object_id] = data;
if (room_object_table_pc_ == 0 || room_object_data_base_pc_ == 0) {
return;
}
uint32_t object_offset = room_object_data_base_pc_ + kRoomObjectStride * static_cast<uint32_t>(object_id);
EnsureBufferCapacity(object_offset + static_cast<uint32_t>(data.size()));
std::memcpy(backing_buffer_.data() + object_offset, data.data(), data.size());
std::memcpy(mutable_data() + object_offset, data.data(), data.size());
uint32_t snes_offset = PcToSnes(object_offset);
uint32_t entry = room_object_table_pc_ + static_cast<uint32_t>(object_id) * 3;
EnsureBufferCapacity(entry + 3);
backing_buffer_[entry] = static_cast<uint8_t>(snes_offset & 0xFF);
backing_buffer_[entry + 1] = static_cast<uint8_t>((snes_offset >> 8) & 0xFF);
backing_buffer_[entry + 2] = static_cast<uint8_t>((snes_offset >> 16) & 0xFF);
mutable_data()[entry] = backing_buffer_[entry];
mutable_data()[entry + 1] = backing_buffer_[entry + 1];
mutable_data()[entry + 2] = backing_buffer_[entry + 2];
}
void MockRom::SetMockGraphicsData(const std::vector<uint8_t>& data) {
mock_graphics_data_ = data;
if (auto* gfx_buffer = mutable_graphics_buffer(); gfx_buffer != nullptr) {
gfx_buffer->assign(data.begin(), data.end());
}
}
void MockRom::EnsureBufferCapacity(uint32_t size) {
if (size <= backing_buffer_.size()) {
return;
}
auto old_size = backing_buffer_.size();
backing_buffer_.resize(size, 0);
Expand(static_cast<int>(size));
std::memcpy(mutable_data(), backing_buffer_.data(), old_size);
}
void MockRom::InitializeMemoryLayout() {
if (backing_buffer_.empty()) {
return;
}
room_header_table_pc_ = SnesToPc(0x040000);
room_header_data_base_pc_ = SnesToPc(0x040000 + 0x1000);
room_object_table_pc_ = SnesToPc(0x050000);
room_object_data_base_pc_ = SnesToPc(0x050000 + 0x2000);
EnsureBufferCapacity(room_header_table_pc_ + 2);
EnsureBufferCapacity(room_object_table_pc_ + 3);
uint32_t header_table_snes = PcToSnes(room_header_table_pc_);
EnsureBufferCapacity(kRoomHeaderPointer + 3);
backing_buffer_[kRoomHeaderPointer] = static_cast<uint8_t>(header_table_snes & 0xFF);
backing_buffer_[kRoomHeaderPointer + 1] = static_cast<uint8_t>((header_table_snes >> 8) & 0xFF);
backing_buffer_[kRoomHeaderPointer + 2] = static_cast<uint8_t>((header_table_snes >> 16) & 0xFF);
mutable_data()[kRoomHeaderPointer] = backing_buffer_[kRoomHeaderPointer];
mutable_data()[kRoomHeaderPointer + 1] = backing_buffer_[kRoomHeaderPointer + 1];
mutable_data()[kRoomHeaderPointer + 2] = backing_buffer_[kRoomHeaderPointer + 2];
EnsureBufferCapacity(kRoomHeaderPointerBank + 1);
backing_buffer_[kRoomHeaderPointerBank] = static_cast<uint8_t>((header_table_snes >> 16) & 0xFF);
mutable_data()[kRoomHeaderPointerBank] = backing_buffer_[kRoomHeaderPointerBank];
uint32_t object_table_snes = PcToSnes(room_object_table_pc_);
EnsureBufferCapacity(room_object_pointer + 3);
backing_buffer_[room_object_pointer] = static_cast<uint8_t>(object_table_snes & 0xFF);
backing_buffer_[room_object_pointer + 1] = static_cast<uint8_t>((object_table_snes >> 8) & 0xFF);
backing_buffer_[room_object_pointer + 2] = static_cast<uint8_t>((object_table_snes >> 16) & 0xFF);
mutable_data()[room_object_pointer] = backing_buffer_[room_object_pointer];
mutable_data()[room_object_pointer + 1] = backing_buffer_[room_object_pointer + 1];
mutable_data()[room_object_pointer + 2] = backing_buffer_[room_object_pointer + 2];
for (const auto& [room_id, bytes] : mock_room_data_) {
uint32_t offset = room_header_data_base_pc_ + kRoomHeaderStride * static_cast<uint32_t>(room_id);
EnsureBufferCapacity(offset + static_cast<uint32_t>(bytes.size()));
std::memcpy(backing_buffer_.data() + offset, bytes.data(), bytes.size());
std::memcpy(mutable_data() + offset, bytes.data(), bytes.size());
uint32_t snes = PcToSnes(offset);
uint32_t entry = room_header_table_pc_ + static_cast<uint32_t>(room_id) * 2;
EnsureBufferCapacity(entry + 2);
backing_buffer_[entry] = static_cast<uint8_t>(snes & 0xFF);
backing_buffer_[entry + 1] = static_cast<uint8_t>((snes >> 8) & 0xFF);
mutable_data()[entry] = backing_buffer_[entry];
mutable_data()[entry + 1] = backing_buffer_[entry + 1];
}
for (const auto& [object_id, bytes] : mock_object_data_) {
uint32_t offset = room_object_data_base_pc_ + kRoomObjectStride * static_cast<uint32_t>(object_id);
EnsureBufferCapacity(offset + static_cast<uint32_t>(bytes.size()));
std::memcpy(backing_buffer_.data() + offset, bytes.data(), bytes.size());
std::memcpy(mutable_data() + offset, bytes.data(), bytes.size());
uint32_t snes = PcToSnes(offset);
uint32_t entry = room_object_table_pc_ + static_cast<uint32_t>(object_id) * 3;
EnsureBufferCapacity(entry + 3);
backing_buffer_[entry] = static_cast<uint8_t>(snes & 0xFF);
backing_buffer_[entry + 1] = static_cast<uint8_t>((snes >> 8) & 0xFF);
backing_buffer_[entry + 2] = static_cast<uint8_t>((snes >> 16) & 0xFF);
mutable_data()[entry] = backing_buffer_[entry];
mutable_data()[entry + 1] = backing_buffer_[entry + 1];
mutable_data()[entry + 2] = backing_buffer_[entry + 2];
}
}
bool MockRom::ValidateRoomData(int room_id) const {
return mock_room_data_.find(room_id) != mock_room_data_.end();
}
bool MockRom::ValidateObjectData(int object_id) const {
return mock_object_data_.find(object_id) != mock_object_data_.end();
}
// Test cases
TEST_F(DungeonEditorIntegrationTest, ObjectParsingTest) {
EXPECT_TRUE(TestObjectParsing().ok());
}
TEST_F(DungeonEditorIntegrationTest, ObjectRenderingTest) {
EXPECT_TRUE(TestObjectRendering().ok());
}
TEST_F(DungeonEditorIntegrationTest, RoomGraphicsTest) {
EXPECT_TRUE(TestRoomGraphics().ok());
}
TEST_F(DungeonEditorIntegrationTest, PaletteHandlingTest) {
EXPECT_TRUE(TestPaletteHandling().ok());
}
TEST_F(DungeonEditorIntegrationTest, MockRomValidation) {
EXPECT_TRUE(static_cast<MockRom*>(mock_rom_.get())->ValidateRoomData(kTestRoomId));
EXPECT_TRUE(static_cast<MockRom*>(mock_rom_.get())->ValidateObjectData(kTestObjectId));
auto encoded = room.EncodeObjects();
EXPECT_FALSE(encoded.empty());
EXPECT_EQ(encoded[encoded.size()-1], 0xFF); // Terminator
}
} // namespace test
} // namespace yaze
} // namespace yaze

View File

@@ -1,93 +1,42 @@
#ifndef YAZE_TEST_INTEGRATION_DUNGEON_EDITOR_TEST_H
#define YAZE_TEST_INTEGRATION_DUNGEON_EDITOR_TEST_H
#include <cstdint>
#include <map>
#include <memory>
#include <string>
#include "absl/status/status.h"
#include "app/editor/dungeon/dungeon_editor.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room.h"
#include "gtest/gtest.h"
namespace yaze {
namespace test {
class MockRom;
/**
* @brief Integration test framework for dungeon editor components
*
* This class provides a comprehensive testing framework for the dungeon editor,
* allowing modular testing of individual components and their interactions.
* @brief Integration test framework using real ROM data
*/
class DungeonEditorIntegrationTest : public ::testing::Test {
protected:
void SetUp() override;
void TearDown() override;
void SetUp() override {
// Use the real ROM
rom_ = std::make_unique<Rom>();
ASSERT_TRUE(rom_->LoadFromFile("build/bin/zelda3.sfc").ok());
dungeon_editor_ = std::make_unique<editor::DungeonEditor>();
dungeon_editor_->set_rom(rom_.get());
}
// Test data setup
absl::Status CreateMockRom();
absl::Status LoadTestRoomData();
// Component testing helpers
absl::Status TestObjectParsing();
absl::Status TestObjectRendering();
absl::Status TestRoomGraphics();
absl::Status TestPaletteHandling();
// Mock data generators
std::vector<uint8_t> GenerateMockRoomHeader(int room_id);
std::vector<uint8_t> GenerateMockObjectData();
std::vector<uint8_t> GenerateMockGraphicsData();
void TearDown() override {
dungeon_editor_.reset();
rom_.reset();
}
std::unique_ptr<MockRom> mock_rom_;
std::unique_ptr<Rom> rom_;
std::unique_ptr<editor::DungeonEditor> dungeon_editor_;
// Test constants
static constexpr int kTestRoomId = 0x01;
static constexpr int kTestObjectId = 0x10;
static constexpr size_t kMockRomSize = 0x200000; // 2MB mock ROM
};
/**
* @brief Mock ROM class for testing without real ROM files
*/
class MockRom : public Rom {
public:
MockRom() = default;
// Test data injection
absl::Status SetMockData(const std::vector<uint8_t>& data);
absl::Status LoadAndOwnData(const std::vector<uint8_t>& data);
void SetMockRoomData(int room_id, const std::vector<uint8_t>& data);
void SetMockObjectData(int object_id, const std::vector<uint8_t>& data);
void SetMockGraphicsData(const std::vector<uint8_t>& data);
// Validation helpers
bool ValidateRoomData(int room_id) const;
bool ValidateObjectData(int object_id) const;
private:
void EnsureBufferCapacity(uint32_t size);
void InitializeMemoryLayout();
std::vector<uint8_t> backing_buffer_;
std::map<int, std::vector<uint8_t>> mock_room_data_;
std::map<int, std::vector<uint8_t>> mock_object_data_;
std::vector<uint8_t> mock_graphics_data_;
uint32_t room_header_table_pc_ = 0;
uint32_t room_header_data_base_pc_ = 0;
uint32_t room_object_table_pc_ = 0;
uint32_t room_object_data_base_pc_ = 0;
static constexpr uint32_t kRoomHeaderStride = 0x40;
static constexpr uint32_t kRoomObjectStride = 0x100;
};
} // namespace test
} // namespace yaze
#endif // YAZE_TEST_INTEGRATION_DUNGEON_EDITOR_TEST_H
#endif // YAZE_TEST_INTEGRATION_DUNGEON_EDITOR_TEST_H