19 : rom_(rom), game_data_(game_data) {}
25 return absl::FailedPreconditionError(
"ROM not loaded");
37 if (!data_status.ok()) {
44 snes_ = std::make_unique<emu::Snes>();
45 const std::vector<uint8_t>& rom_data =
rom_->
vector();
46 snes_->Init(rom_data);
56 return absl::OkStatus();
61 return absl::FailedPreconditionError(
"Service not initialized");
69 return absl::FailedPreconditionError(
"Service not initialized");
72 switch (request.
type) {
87 return absl::InvalidArgumentError(
"Unknown render target type");
92 const std::vector<RenderRequest>& requests) {
93 std::vector<RenderResult> results;
94 results.reserve(requests.size());
96 for (
const auto& request : requests) {
97 auto result =
Render(request);
99 results.push_back(std::move(*result));
103 error_result.
error = std::string(result.status().message());
104 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());
186 return absl::FailedPreconditionError(
"GameData not available");
190 if (palette_id >= dungeon_main_pal_group.size()) {
193 auto palette = dungeon_main_pal_group[palette_id];
208 auto status = drawer.
DrawObject(obj, bg1_buffer, bg2_buffer, dungeon_main_pal_group);
211 result.
error = std::string(status.message());
224 const auto& bg2_pixels = bg2_buffer.
bitmap().data();
225 const auto& bg1_pixels = bg1_buffer.
bitmap().data();
229 int src_idx = y * 512 + x;
232 uint8_t pixel = bg1_pixels[src_idx];
234 pixel = bg2_pixels[src_idx];
238 if (pixel > 0 && pixel < palette.size()) {
239 auto color = palette[pixel];
240 rgba_pixels[dst_idx + 0] = color.rgb().x;
241 rgba_pixels[dst_idx + 1] = color.rgb().y;
242 rgba_pixels[dst_idx + 2] = color.rgb().z;
243 rgba_pixels[dst_idx + 3] = 255;
246 rgba_pixels[dst_idx + 3] = 0;
264 result.
error =
"Sprite rendering not yet implemented";
272 result.
error =
"Full room rendering not yet implemented";
278 auto& ppu =
snes_->ppu();
288 if (palette < dungeon_main_pal_group.size()) {
289 auto base_palette = dungeon_main_pal_group[palette];
290 std::optional<gfx::SnesPalette> hud_palette_storage;
294 hud_palette = &*hud_palette_storage;
302 for (
int i = 0; i < 30; ++i) {
303 uint32_t addr = kSpriteAuxPc + i * 2;
316 std::vector<uint8_t> linear_data(gfx_buffer.begin(), gfx_buffer.end());
320 for (
size_t i = 0; i < planar_data.size() / 2 && i < 0x8000; ++i) {
321 ppu.vram[i] = planar_data[i * 2] | (planar_data[i * 2 + 1] << 8);
325 snes_->Write(0x002105, 0x09);
326 snes_->Write(0x002107, 0x40);
327 snes_->Write(0x002108, 0x48);
328 snes_->Write(0x00210B, 0x00);
329 snes_->Write(0x00212C, 0x03);
330 snes_->Write(0x002100, 0x0F);
338 auto& ppu =
snes_->ppu();
342 if (palette_id >= 0 &&
343 palette_id <
static_cast<int>(dungeon_main_pal_group.size())) {
344 auto palette = dungeon_main_pal_group[palette_id];
345 std::optional<gfx::SnesPalette> hud_palette_storage;
349 hud_palette = &*hud_palette_storage;
361 for (
int i = 0; i < 11; ++i) {
364 uint8_t lo = wram_addr & 0xFF;
365 uint8_t mid = (wram_addr >> 8) & 0xFF;
366 uint8_t hi = (wram_addr >> 16) & 0xFF;
369 snes_->Write(0x7E0000 | zp_addr, lo);
370 snes_->Write(0x7E0000 | (zp_addr + 1), mid);
371 snes_->Write(0x7E0000 | (zp_addr + 2), hi);
383 auto& apu =
snes_->apu();
384 apu.out_ports_[0] = 0xAA;
385 apu.out_ports_[1] = 0xBB;
386 apu.out_ports_[2] = 0x00;
387 apu.out_ports_[3] = 0x00;
391 int object_id,
int* data_offset) {
393 uint32_t data_table_snes = 0;
394 uint32_t handler_table_snes = 0;
396 if (object_id < 0x100) {
399 }
else if (object_id < 0x200) {
411 uint32_t data_table_pc =
SnesToPc(data_table_snes);
412 uint32_t handler_table_pc =
SnesToPc(handler_table_snes);
414 if (data_table_pc + 1 >=
rom_->
size() ||
415 handler_table_pc + 1 >=
rom_->
size()) {
416 return absl::OutOfRangeError(
"Object ID out of bounds");
419 *data_offset = rom_data[data_table_pc] | (rom_data[data_table_pc + 1] << 8);
421 rom_data[handler_table_pc] | (rom_data[handler_table_pc + 1] << 8);
423 if (handler_addr == 0x0000) {
424 return absl::NotFoundError(
"Object has no drawing routine");
433 auto& cpu =
snes_->cpu();
447 const uint16_t trap_addr = 0xFF00;
448 snes_->Write(0x01FF00, 0xDB);
451 uint16_t sp = cpu.SP();
452 snes_->Write(0x010000 | sp--, 0x01);
453 snes_->Write(0x010000 | sp--, (trap_addr - 1) >> 8);
454 snes_->Write(0x010000 | sp--, (trap_addr - 1) & 0xFF);
457 cpu.PC = handler_addr;
460 int max_opcodes = 100000;
462 auto& apu =
snes_->apu();
464 while (opcodes < max_opcodes) {
465 uint32_t current_addr = (cpu.PB << 16) | cpu.PC;
466 uint8_t current_opcode =
snes_->Read(current_addr);
467 if (current_opcode == 0xDB) {
472 if ((opcodes & 0x3F) == 0) {
473 apu.out_ports_[0] = 0xAA;
474 apu.out_ports_[1] = 0xBB;
481 if (opcodes >= max_opcodes) {
482 return absl::DeadlineExceededError(
"Handler execution timeout");
485 return absl::OkStatus();
489 auto& ppu =
snes_->ppu();
492 for (uint32_t i = 0; i < 0x800; i++) {
495 ppu.vram[0x4000 + i] = lo | (hi << 8);
497 for (uint32_t i = 0; i < 0x800; i++) {
500 ppu.vram[0x4800 + i] = lo | (hi << 8);
504 ppu.HandleFrameStart();
505 for (
int line = 0; line < 224; line++) {
513 std::vector<uint8_t> rgba(256 * 224 * 4);
517 auto& ppu =
snes_->ppu();
519 for (
int y = 0; y < 224; ++y) {
520 for (
int x = 0; x < 256; ++x) {
521 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(uint8_t entrance_blockset=0xFF)
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)