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];
212 drawer.
DrawObject(obj, bg1_buffer, bg2_buffer, dungeon_main_pal_group);
215 result.
error = std::string(status.message());
228 const auto& bg2_pixels = bg2_buffer.
bitmap().data();
229 const auto& bg1_pixels = bg1_buffer.
bitmap().data();
233 int src_idx = y * 512 + x;
236 uint8_t pixel = bg1_pixels[src_idx];
238 pixel = bg2_pixels[src_idx];
242 if (pixel > 0 && pixel < palette.size()) {
243 auto color = palette[pixel];
244 rgba_pixels[dst_idx + 0] = color.rgb().x;
245 rgba_pixels[dst_idx + 1] = color.rgb().y;
246 rgba_pixels[dst_idx + 2] = color.rgb().z;
247 rgba_pixels[dst_idx + 3] = 255;
250 rgba_pixels[dst_idx + 3] = 0;
268 result.
error =
"Sprite rendering not yet implemented";
276 result.
error =
"Full room rendering not yet implemented";
282 auto& ppu =
snes_->ppu();
293 if (palette < dungeon_main_pal_group.size()) {
294 auto base_palette = dungeon_main_pal_group[palette];
295 std::optional<gfx::SnesPalette> hud_palette_storage;
299 hud_palette = &*hud_palette_storage;
307 for (
int i = 0; i < 30; ++i) {
308 uint32_t addr = kSpriteAuxPc + i * 2;
316 if (blockset == 0xFF) {
325 std::vector<uint8_t> linear_data(gfx_buffer.begin(), gfx_buffer.end());
329 for (
size_t i = 0; i < planar_data.size() / 2 && i < 0x8000; ++i) {
330 ppu.vram[i] = planar_data[i * 2] | (planar_data[i * 2 + 1] << 8);
334 snes_->Write(0x002105, 0x09);
335 snes_->Write(0x002107, 0x40);
336 snes_->Write(0x002108, 0x48);
337 snes_->Write(0x00210B, 0x00);
338 snes_->Write(0x00212C, 0x03);
339 snes_->Write(0x002100, 0x0F);
347 auto& ppu =
snes_->ppu();
352 if (palette_id >= 0 &&
353 palette_id <
static_cast<int>(dungeon_main_pal_group.size())) {
354 auto palette = dungeon_main_pal_group[palette_id];
355 std::optional<gfx::SnesPalette> hud_palette_storage;
359 hud_palette = &*hud_palette_storage;
371 for (
int i = 0; i < 11; ++i) {
374 uint8_t lo = wram_addr & 0xFF;
375 uint8_t mid = (wram_addr >> 8) & 0xFF;
376 uint8_t hi = (wram_addr >> 16) & 0xFF;
379 snes_->Write(0x7E0000 | zp_addr, lo);
380 snes_->Write(0x7E0000 | (zp_addr + 1), mid);
381 snes_->Write(0x7E0000 | (zp_addr + 2), hi);
393 auto& apu =
snes_->apu();
394 apu.out_ports_[0] = 0xAA;
395 apu.out_ports_[1] = 0xBB;
396 apu.out_ports_[2] = 0x00;
397 apu.out_ports_[3] = 0x00;
401 int object_id,
int* data_offset) {
403 uint32_t data_table_snes = 0;
404 uint32_t handler_table_snes = 0;
406 if (object_id < 0x100) {
409 }
else if (object_id < 0x200) {
421 uint32_t data_table_pc =
SnesToPc(data_table_snes);
422 uint32_t handler_table_pc =
SnesToPc(handler_table_snes);
424 if (data_table_pc + 1 >=
rom_->
size() ||
425 handler_table_pc + 1 >=
rom_->
size()) {
426 return absl::OutOfRangeError(
"Object ID out of bounds");
429 *data_offset = rom_data[data_table_pc] | (rom_data[data_table_pc + 1] << 8);
431 rom_data[handler_table_pc] | (rom_data[handler_table_pc + 1] << 8);
433 if (handler_addr == 0x0000) {
434 return absl::NotFoundError(
"Object has no drawing routine");
443 auto& cpu =
snes_->cpu();
457 const uint16_t trap_addr = 0xFF00;
458 snes_->Write(0x01FF00, 0xDB);
461 uint16_t sp = cpu.SP();
462 snes_->Write(0x010000 | sp--, 0x01);
463 snes_->Write(0x010000 | sp--, (trap_addr - 1) >> 8);
464 snes_->Write(0x010000 | sp--, (trap_addr - 1) & 0xFF);
467 cpu.PC = handler_addr;
470 int max_opcodes = 100000;
472 auto& apu =
snes_->apu();
474 while (opcodes < max_opcodes) {
475 uint32_t current_addr = (cpu.PB << 16) | cpu.PC;
476 uint8_t current_opcode =
snes_->Read(current_addr);
477 if (current_opcode == 0xDB) {
482 if ((opcodes & 0x3F) == 0) {
483 apu.out_ports_[0] = 0xAA;
484 apu.out_ports_[1] = 0xBB;
491 if (opcodes >= max_opcodes) {
492 return absl::DeadlineExceededError(
"Handler execution timeout");
495 return absl::OkStatus();
499 auto& ppu =
snes_->ppu();
502 for (uint32_t i = 0; i < 0x800; i++) {
505 ppu.vram[0x4000 + i] = lo | (hi << 8);
507 for (uint32_t i = 0; i < 0x800; i++) {
510 ppu.vram[0x4800 + i] = lo | (hi << 8);
514 ppu.HandleFrameStart();
515 for (
int line = 0; line < 224; line++) {
523 std::vector<uint8_t> rgba(256 * 224 * 4);
527 auto& ppu =
snes_->ppu();
529 for (
int y = 0; y < 224; ++y) {
530 for (
int x = 0; x < 256; ++x) {
531 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.
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(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)