18 : rom_(rom), game_data_(game_data) {}
24 return absl::FailedPreconditionError(
"ROM not loaded");
28 snes_ = std::make_unique<emu::Snes>();
29 const std::vector<uint8_t>& rom_data =
rom_->
vector();
30 snes_->Init(rom_data);
40 return absl::OkStatus();
45 return absl::FailedPreconditionError(
"Service not initialized");
53 return absl::FailedPreconditionError(
"Service not initialized");
56 switch (request.
type) {
71 return absl::InvalidArgumentError(
"Unknown render target type");
76 const std::vector<RenderRequest>& requests) {
77 std::vector<RenderResult> results;
78 results.reserve(requests.size());
80 for (
const auto& request : requests) {
81 auto result =
Render(request);
83 results.push_back(std::move(*result));
87 error_result.
error = std::string(result.status().message());
88 results.push_back(std::move(error_result));
126 if (!handler_result.ok()) {
128 result.
error = std::string(handler_result.status().message());
131 int handler_addr = *handler_result;
134 int tilemap_pos = (req.
y * 0x80) + (req.
x * 2);
140 result.
error = std::string(status.message());
170 return absl::FailedPreconditionError(
"GameData not available");
174 if (palette_id >= dungeon_main_pal_group.size()) {
177 auto palette = dungeon_main_pal_group[palette_id];
192 auto status = drawer.
DrawObject(obj, bg1_buffer, bg2_buffer, dungeon_main_pal_group);
195 result.
error = std::string(status.message());
208 const auto& bg2_pixels = bg2_buffer.
bitmap().data();
209 const auto& bg1_pixels = bg1_buffer.
bitmap().data();
213 int src_idx = y * 512 + x;
216 uint8_t pixel = bg1_pixels[src_idx];
218 pixel = bg2_pixels[src_idx];
222 if (pixel > 0 && pixel < palette.size()) {
223 auto color = palette[pixel];
224 rgba_pixels[dst_idx + 0] = color.rgb().x;
225 rgba_pixels[dst_idx + 1] = color.rgb().y;
226 rgba_pixels[dst_idx + 2] = color.rgb().z;
227 rgba_pixels[dst_idx + 3] = 255;
230 rgba_pixels[dst_idx + 3] = 0;
248 result.
error =
"Sprite rendering not yet implemented";
256 result.
error =
"Full room rendering not yet implemented";
262 auto& ppu =
snes_->ppu();
271 if (palette < dungeon_main_pal_group.size()) {
272 auto base_palette = dungeon_main_pal_group[palette];
273 for (
size_t i = 0; i < base_palette.size() && i < 90; ++i) {
274 ppu.cgram[i] = base_palette[i].snes();
280 for (
int i = 0; i < 30; ++i) {
281 uint32_t addr = kSpriteAuxPc + i * 2;
294 std::vector<uint8_t> linear_data(gfx_buffer.begin(), gfx_buffer.end());
298 for (
size_t i = 0; i < planar_data.size() / 2 && i < 0x8000; ++i) {
299 ppu.vram[i] = planar_data[i * 2] | (planar_data[i * 2 + 1] << 8);
303 snes_->Write(0x002105, 0x09);
304 snes_->Write(0x002107, 0x40);
305 snes_->Write(0x002108, 0x48);
306 snes_->Write(0x00210B, 0x00);
307 snes_->Write(0x00212C, 0x03);
308 snes_->Write(0x002100, 0x0F);
316 auto& ppu =
snes_->ppu();
320 if (palette_id >= 0 &&
321 palette_id <
static_cast<int>(dungeon_main_pal_group.size())) {
322 auto palette = dungeon_main_pal_group[palette_id];
323 for (
size_t i = 0; i < palette.size() && i < 90; ++i) {
324 ppu.cgram[i] = palette[i].snes();
335 for (
int i = 0; i < 11; ++i) {
338 uint8_t lo = wram_addr & 0xFF;
339 uint8_t mid = (wram_addr >> 8) & 0xFF;
340 uint8_t hi = (wram_addr >> 16) & 0xFF;
343 snes_->Write(0x7E0000 | zp_addr, lo);
344 snes_->Write(0x7E0000 | (zp_addr + 1), mid);
345 snes_->Write(0x7E0000 | (zp_addr + 2), hi);
357 auto& apu =
snes_->apu();
358 apu.out_ports_[0] = 0xAA;
359 apu.out_ports_[1] = 0xBB;
360 apu.out_ports_[2] = 0x00;
361 apu.out_ports_[3] = 0x00;
365 int object_id,
int* data_offset) {
367 uint32_t data_table_snes = 0;
368 uint32_t handler_table_snes = 0;
370 if (object_id < 0x100) {
373 }
else if (object_id < 0x200) {
385 uint32_t data_table_pc =
SnesToPc(data_table_snes);
386 uint32_t handler_table_pc =
SnesToPc(handler_table_snes);
388 if (data_table_pc + 1 >=
rom_->
size() ||
389 handler_table_pc + 1 >=
rom_->
size()) {
390 return absl::OutOfRangeError(
"Object ID out of bounds");
393 *data_offset = rom_data[data_table_pc] | (rom_data[data_table_pc + 1] << 8);
395 rom_data[handler_table_pc] | (rom_data[handler_table_pc + 1] << 8);
397 if (handler_addr == 0x0000) {
398 return absl::NotFoundError(
"Object has no drawing routine");
407 auto& cpu =
snes_->cpu();
421 const uint16_t trap_addr = 0xFF00;
422 snes_->Write(0x01FF00, 0xDB);
425 uint16_t sp = cpu.SP();
426 snes_->Write(0x010000 | sp--, 0x01);
427 snes_->Write(0x010000 | sp--, (trap_addr - 1) >> 8);
428 snes_->Write(0x010000 | sp--, (trap_addr - 1) & 0xFF);
431 cpu.PC = handler_addr;
434 int max_opcodes = 100000;
436 auto& apu =
snes_->apu();
438 while (opcodes < max_opcodes) {
439 uint32_t current_addr = (cpu.PB << 16) | cpu.PC;
440 uint8_t current_opcode =
snes_->Read(current_addr);
441 if (current_opcode == 0xDB) {
446 if ((opcodes & 0x3F) == 0) {
447 apu.out_ports_[0] = 0xAA;
448 apu.out_ports_[1] = 0xBB;
455 if (opcodes >= max_opcodes) {
456 return absl::DeadlineExceededError(
"Handler execution timeout");
459 return absl::OkStatus();
463 auto& ppu =
snes_->ppu();
466 for (uint32_t i = 0; i < 0x800; i++) {
469 ppu.vram[0x4000 + i] = lo | (hi << 8);
471 for (uint32_t i = 0; i < 0x800; i++) {
474 ppu.vram[0x4800 + i] = lo | (hi << 8);
478 ppu.HandleFrameStart();
479 for (
int line = 0; line < 224; line++) {
487 std::vector<uint8_t> rgba(256 * 224 * 4);
491 auto& ppu =
snes_->ppu();
493 for (
int y = 0; y < 224; ++y) {
494 for (
int x = 0; x < 256; ++x) {
495 int idx = (y * 256 + x) * 4;
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
const auto & vector() const
absl::StatusOr< RenderResult > RenderDungeonObjectStatic(const RenderRequest &req)
zelda3::GameData * game_data_
absl::Status ExecuteHandler(int handler_addr, int data_offset, int tilemap_pos)
std::vector< uint8_t > ExtractPixelsFromPpu()
absl::Status Initialize()
void ClearTilemapBuffers()
absl::StatusOr< RenderResult > Render(const RenderRequest &request)
void InjectRoomContext(int room_id, uint8_t blockset, uint8_t palette)
absl::StatusOr< RenderResult > RenderDungeonObject(const RenderRequest &req)
absl::StatusOr< RenderResult > RenderFullRoom(const RenderRequest &req)
absl::StatusOr< std::vector< RenderResult > > RenderBatch(const std::vector< RenderRequest > &requests)
absl::StatusOr< RenderResult > RenderSprite(const RenderRequest &req)
std::unique_ptr< SaveStateManager > state_manager_
void LoadPaletteIntoCgram(int palette_id)
absl::Status GenerateBaselineStates()
absl::StatusOr< int > LookupHandlerAddress(int object_id, int *data_offset)
std::unique_ptr< emu::Snes > snes_
EmulatorRenderService(Rom *rom, zelda3::GameData *game_data=nullptr)
void LoadGraphicsIntoVram(uint8_t blockset)
void InitializeTilemapPointers()
void EnsureBitmapInitialized()
Draws dungeon objects to background buffers using game patterns.
void InitializeDrawRoutines()
Initialize draw routine registry Must be called before drawing objects.
absl::Status DrawObject(const RoomObject &object, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr)
Draw a room object to background buffers.
const std::array< uint8_t, 0x10000 > & get_gfx_buffer() const
void CopyRoomGraphicsToBuffer()
void LoadRoomGraphics(uint8_t entrance_blockset=0xFF)
void SetGameData(GameData *data)
struct snes_color snes_color
SNES color in 15-bit RGB format (BGR555)
constexpr uint32_t kType3HandlerTable
constexpr uint32_t kType2HandlerTable
constexpr uint32_t kType2DataTable
constexpr uint32_t kType1DataTable
constexpr uint32_t kSpriteAuxPalettes
constexpr uint32_t kType1HandlerTable
constexpr uint32_t kType3DataTable
constexpr uint32_t kBG1TilemapBuffer
constexpr uint32_t kTilemapRowStride
constexpr uint32_t kBG2TilemapBuffer
constexpr uint8_t kTilemapPointers[]
constexpr uint32_t kTilemapBufferSize
constexpr uint32_t kRoomId
uint32_t SnesToPc(uint32_t snes_addr)
std::vector< uint8_t > ConvertLinear8bppToPlanar4bpp(const std::vector< uint8_t > &linear_data)
Room LoadRoomFromRom(Rom *rom, int room_id)
SNES color in 15-bit RGB format (BGR555)
std::vector< uint8_t > rgba_pixels
bool used_static_fallback
PaletteGroup dungeon_main
gfx::PaletteGroupMap palette_groups