20 : rom_(rom), game_data_(game_data) {}
26 return absl::FailedPreconditionError(
"ROM not loaded");
38 if (!data_status.ok()) {
45 snes_ = std::make_unique<emu::Snes>();
46 const std::vector<uint8_t>& rom_data =
rom_->
vector();
47 snes_->Init(rom_data);
57 return absl::OkStatus();
62 return absl::FailedPreconditionError(
"Service not initialized");
70 return absl::FailedPreconditionError(
"Service not initialized");
73 switch (request.
type) {
88 return absl::InvalidArgumentError(
"Unknown render target type");
93 const std::vector<RenderRequest>& requests) {
94 std::vector<RenderResult> results;
95 results.reserve(requests.size());
97 for (
const auto& request : requests) {
98 auto result =
Render(request);
100 results.push_back(std::move(*result));
104 error_result.
error = std::string(result.status().message());
105 results.push_back(std::move(error_result));
142 if (!handler_result.ok()) {
144 result.
error = std::string(handler_result.status().message());
147 int handler_addr = *handler_result;
150 int tilemap_pos = (req.
y * 0x80) + (req.
x * 2);
156 result.
error = std::string(status.message());
189 return absl::FailedPreconditionError(
"GameData not available");
193 if (palette_id >= dungeon_main_pal_group.size()) {
196 auto palette = dungeon_main_pal_group[palette_id];
213 drawer.
DrawObject(obj, bg1_buffer, bg2_buffer, dungeon_main_pal_group);
216 result.
error = std::string(status.message());
229 const auto& bg2_pixels = bg2_buffer.
bitmap().data();
230 const auto& bg1_pixels = bg1_buffer.
bitmap().data();
234 int src_idx = y * 512 + x;
237 uint8_t pixel = bg1_pixels[src_idx];
239 pixel = bg2_pixels[src_idx];
243 if (pixel > 0 && pixel < palette.size()) {
244 auto color = palette[pixel];
245 rgba_pixels[dst_idx + 0] = color.rgb().x;
246 rgba_pixels[dst_idx + 1] = color.rgb().y;
247 rgba_pixels[dst_idx + 2] = color.rgb().z;
248 rgba_pixels[dst_idx + 3] = 255;
251 rgba_pixels[dst_idx + 3] = 0;
269 result.
error =
"Sprite rendering not yet implemented";
277 result.
error =
"Full room rendering not yet implemented";
283 auto& ppu =
snes_->ppu();
294 if (palette < dungeon_main_pal_group.size()) {
295 auto base_palette = dungeon_main_pal_group[palette];
296 std::optional<gfx::SnesPalette> hud_palette_storage;
300 hud_palette = &*hud_palette_storage;
308 for (
int i = 0; i < 30; ++i) {
309 uint32_t addr = kSpriteAuxPc + i * 2;
317 if (blockset == 0xFF) {
326 std::vector<uint8_t> linear_data(gfx_buffer.begin(), gfx_buffer.end());
330 for (
size_t i = 0; i < planar_data.size() / 2 && i < 0x8000; ++i) {
331 ppu.vram[i] = planar_data[i * 2] | (planar_data[i * 2 + 1] << 8);
335 snes_->Write(0x002105, 0x09);
336 snes_->Write(0x002107, 0x40);
337 snes_->Write(0x002108, 0x48);
338 snes_->Write(0x00210B, 0x00);
339 snes_->Write(0x00212C, 0x03);
340 snes_->Write(0x002100, 0x0F);
348 auto& ppu =
snes_->ppu();
353 if (palette_id >= 0 &&
354 palette_id <
static_cast<int>(dungeon_main_pal_group.size())) {
355 auto palette = dungeon_main_pal_group[palette_id];
356 std::optional<gfx::SnesPalette> hud_palette_storage;
360 hud_palette = &*hud_palette_storage;
372 for (
int i = 0; i < 11; ++i) {
375 uint8_t lo = wram_addr & 0xFF;
376 uint8_t mid = (wram_addr >> 8) & 0xFF;
377 uint8_t hi = (wram_addr >> 16) & 0xFF;
380 snes_->Write(0x7E0000 | zp_addr, lo);
381 snes_->Write(0x7E0000 | (zp_addr + 1), mid);
382 snes_->Write(0x7E0000 | (zp_addr + 2), hi);
394 auto& apu =
snes_->apu();
395 apu.out_ports_[0] = 0xAA;
396 apu.out_ports_[1] = 0xBB;
397 apu.out_ports_[2] = 0x00;
398 apu.out_ports_[3] = 0x00;
402 int object_id,
int* data_offset) {
404 uint32_t data_table_snes = 0;
405 uint32_t handler_table_snes = 0;
407 if (object_id < 0x100) {
410 }
else if (object_id < 0x200) {
422 uint32_t data_table_pc =
SnesToPc(data_table_snes);
423 uint32_t handler_table_pc =
SnesToPc(handler_table_snes);
425 if (data_table_pc + 1 >=
rom_->
size() ||
426 handler_table_pc + 1 >=
rom_->
size()) {
427 return absl::OutOfRangeError(
"Object ID out of bounds");
430 *data_offset = rom_data[data_table_pc] | (rom_data[data_table_pc + 1] << 8);
432 rom_data[handler_table_pc] | (rom_data[handler_table_pc + 1] << 8);
434 if (handler_addr == 0x0000) {
435 return absl::NotFoundError(
"Object has no drawing routine");
444 auto& cpu =
snes_->cpu();
458 const uint16_t trap_addr = 0xFF00;
459 snes_->Write(0x01FF00, 0xDB);
462 uint16_t sp = cpu.SP();
463 snes_->Write(0x010000 | sp--, 0x01);
464 snes_->Write(0x010000 | sp--, (trap_addr - 1) >> 8);
465 snes_->Write(0x010000 | sp--, (trap_addr - 1) & 0xFF);
468 cpu.PC = handler_addr;
471 int max_opcodes = 100000;
473 auto& apu =
snes_->apu();
475 while (opcodes < max_opcodes) {
476 uint32_t current_addr = (cpu.PB << 16) | cpu.PC;
477 uint8_t current_opcode =
snes_->Read(current_addr);
478 if (current_opcode == 0xDB) {
483 if ((opcodes & 0x3F) == 0) {
484 apu.out_ports_[0] = 0xAA;
485 apu.out_ports_[1] = 0xBB;
492 if (opcodes >= max_opcodes) {
493 return absl::DeadlineExceededError(
"Handler execution timeout");
496 return absl::OkStatus();
500 auto& ppu =
snes_->ppu();
503 for (uint32_t i = 0; i < 0x800; i++) {
506 ppu.vram[0x4000 + i] = lo | (hi << 8);
508 for (uint32_t i = 0; i < 0x800; i++) {
511 ppu.vram[0x4800 + i] = lo | (hi << 8);
515 ppu.HandleFrameStart();
516 for (
int line = 0; line < 224; line++) {
524 std::vector<uint8_t> rgba(256 * 224 * 4);
528 auto& ppu =
snes_->ppu();
530 for (
int y = 0; y < 224; ++y) {
531 for (
int x = 0; x < 256; ++x) {
532 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()
std::unique_ptr< zelda3::GameData > owned_game_data_
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()
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Draws dungeon objects to background buffers using game patterns.
void InitializeDrawRoutines()
Initialize draw routine registry Must be called before drawing objects.
void SetRoomFloorGraphics(uint8_t floor1, uint8_t floor2)
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, gfx::BackgroundBuffer *layout_bg2=nullptr)
Draw a room object to background buffers.
const std::array< uint8_t, 0x10000 > & get_gfx_buffer() const
void CopyRoomGraphicsToBuffer()
void LoadRoomGraphics(std::optional< uint8_t > entrance_blockset=std::nullopt)
void SetGameData(GameData *data)
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)
absl::Status LoadGameData(Rom &rom, GameData &data, const LoadOptions &options)
Loads all Zelda3-specific game data from a generic ROM.
void LoadDungeonRenderPaletteToCgram(std::span< uint16_t > cgram, const gfx::SnesPalette &dungeon_palette, const gfx::SnesPalette *hud_palette)
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
const SnesPalette & palette_ref(int i) const
gfx::PaletteGroupMap palette_groups
struct snes_color snes_color
SNES color in 15-bit RGB format (BGR555)