3#include "absl/strings/str_format.h"
26 return (bank << 16) | (high << 8) | low;
31 return (snes_addr & 0x7FFF) | ((snes_addr & 0x7F0000) >> 1);
41 const int palette_size =
static_cast<int>(palette.
size());
42 if (palette_size <= 0) {
46 const bool has_explicit_transparent =
47 palette_size >= 16 && (palette_size % 16 == 0);
49 slice.
offset = has_explicit_transparent ? 1 : 0;
50 slice.
length = has_explicit_transparent ? 15 : std::min(palette_size, 15);
57 slice.
length = std::min(palette_size, 15);
113 uint32_t ptr2, uint32_t ptr3,
size_t rom_size) {
114 if (ptr1 > UINT32_MAX - addr || ptr1 + addr >= rom_size ||
115 ptr2 > UINT32_MAX - addr || ptr2 + addr >= rom_size ||
116 ptr3 > UINT32_MAX - addr || ptr3 + addr >= rom_size) {
117 return static_cast<uint32_t
>(rom_size);
145 if (rom.
size() < 1048576 * 2) {
152 return absl::OkStatus();
161 for (
size_t i = 0; i < group.
size(); ++i) {
163 for (
size_t j = 0; j < palette->size(); ++j) {
166 RETURN_IF_ERROR(rom.WriteColor(
167 gfx::GetPaletteAddress(group.name(), i, j), color));
168 color.set_modified(false);
172 return absl::OkStatus();
181 return absl::OkStatus();
185 constexpr uint32_t kTitleStringOffset = 0x7FC0;
186 constexpr uint32_t kTitleStringLength = 20;
188 if (rom.
size() < kTitleStringOffset + kTitleStringLength) {
189 return absl::OutOfRangeError(
"ROM too small for metadata");
193 if (kTitleStringOffset + 0x19 < rom.
size()) {
195 uint8_t version_byte = rom.
data()[kTitleStringOffset + 0x19];
197 (version_byte == 0) ? zelda3_version::JP : zelda3_version::US;
200 auto title_bytes = rom.
ReadByteVector(kTitleStringOffset, kTitleStringLength);
201 if (title_bytes.ok()) {
202 data.
title.assign(title_bytes->begin(), title_bytes->end());
205 return absl::OkStatus();
210 const std::vector<uint8_t>& rom_vec = rom.
vector();
215 if (kVersionConstantsMap.find(data.
version) == kVersionConstantsMap.end()) {
216 return absl::FailedPreconditionError(
"Unsupported ROM version");
219 auto version_constants = kVersionConstantsMap.at(data.
version);
223 if (main_ptr_res.ok()) {
224 uint32_t main_ptr =
SnesToPc(*main_ptr_res);
226 for (
int j = 0; j < 8; j++) {
227 auto val = rom.
ReadByte(main_ptr + (i * 8) + j);
236 for (
int j = 0; j < 4; j++) {
245 for (
int j = 0; j < 4; j++) {
247 rom.
ReadByte(version_constants.kSpriteBlocksetPointer + (i * 4) + j);
255 for (
int j = 0; j < 4; j++) {
257 rom.
ReadByte(version_constants.kDungeonPalettesGroups + (i * 4) + j);
263 return absl::OkStatus();
267 auto version_constants = kVersionConstantsMap.at(data.
version);
274 for (
int j = 0; j < 8; j++) {
282 for (
int j = 0; j < 4; j++) {
290 for (
int j = 0; j < 4; j++) {
292 rom.
WriteByte(version_constants.kSpriteBlocksetPointer + (i * 4) + j,
299 for (
int j = 0; j < 4; j++) {
301 rom.
WriteByte(version_constants.kDungeonPalettesGroups + (i * 4) + j,
306 return absl::OkStatus();
351 uint32_t ptr2, uint32_t ptr3) {
356 if (i >= 115 && i <= 126) {
364 result.
data = *read_res;
372 else if (i == 113 || i == 114 || i >= 218) {
384 0x800, 1, rom.
size());
385 if (decomp_res.ok()) {
386 result.
data = *decomp_res;
401 if (converted_sheet.size() != 4096)
402 converted_sheet.resize(4096, 0);
419 if (!default_palette.
empty()) {
420 ApplyDefaultPalette(data.
gfx_bitmaps[i], default_palette);
423 std::vector<gfx::SnesColor> grayscale;
424 for (
int color_idx = 0; color_idx < 16; ++color_idx) {
425 float val = color_idx / 15.0f;
426 grayscale.emplace_back(ImVec4(val, val, val, 1.0f));
428 if (!grayscale.empty()) {
429 grayscale[0].set_transparent(
true);
435 std::vector<gfx::SnesColor> grayscale;
436 for (
int color_idx = 0; color_idx < 16; ++color_idx) {
437 float val = color_idx / 15.0f;
438 grayscale.emplace_back(ImVec4(val, val, val, 1.0f));
440 if (!grayscale.empty()) {
441 grayscale[0].set_transparent(
true);
451 std::vector<uint8_t> placeholder(4096, 0);
460 if (kVersionConstantsMap.find(data.
version) == kVersionConstantsMap.end()) {
461 return absl::FailedPreconditionError(
462 "Unsupported ROM version for graphics");
464 auto version_constants = kVersionConstantsMap.at(data.
version);
467 version_constants.kOverworldGfxPtr1);
470 version_constants.kOverworldGfxPtr2);
473 version_constants.kOverworldGfxPtr3);
478 auto loading_handle =
479 app::platform::WasmLoadingManager::BeginLoading(
"Loading Graphics");
485 diag.ptr1_loc = gfx_ptr1;
486 diag.ptr2_loc = gfx_ptr2;
487 diag.ptr3_loc = gfx_ptr3;
493 app::platform::WasmLoadingManager::UpdateProgress(
498 auto result =
LoadSheetRaw(rom, i, gfx_ptr1, gfx_ptr2, gfx_ptr3);
501 auto& sd = diag.sheets[i];
503 sd.is_compressed = result.is_compressed;
504 sd.pc_offset = result.pc_offset;
505 sd.decompression_succeeded = result.decompression_succeeded;
506 sd.actual_decomp_size = result.data.size();
507 if (!result.data.empty()) {
508 size_t count = std::min<size_t>(result.data.size(), 8);
509 sd.first_bytes.assign(result.data.begin(), result.data.begin() + count);
511 if (result.is_compressed && !result.is_bpp3) {
512 sd.decomp_size_param = 0x800;
518 LOG_DEBUG(
"Graphics",
"Sheet %d: offset=0x%06X, size=%zu, %s", i,
519 result.pc_offset, result.data.size(),
520 result.decompression_succeeded ?
"OK" :
"FAILED");
525 LOG_INFO(
"Graphics",
"Graphics loading complete. Sheets processed: %d",
529 app::platform::WasmLoadingManager::EndLoading(loading_handle);
532 return absl::OkStatus();
541 std::array<gfx::Bitmap, kNumLinkSheets> link_graphics;
543 auto link_sheet_data_result =
546 if (!link_sheet_data_result.ok()) {
547 return link_sheet_data_result.status();
549 auto link_sheet_8bpp =
551 if (link_sheet_8bpp.size() != 4096)
552 link_sheet_8bpp.resize(4096, 0);
558 return link_graphics;
566 std::vector<uint8_t> sheet;
567 const uint8_t sheets[] = {0x71, 0x72, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE};
570 auto version_constants = kVersionConstantsMap.at(zelda3_version::US);
573 version_constants.kOverworldGfxPtr1);
576 version_constants.kOverworldGfxPtr2);
579 version_constants.kOverworldGfxPtr3);
581 for (
const auto& sheet_id : sheets) {
583 gfx_ptr3, rom.
size());
585 if (offset >= rom.
size()) {
586 return absl::OutOfRangeError(absl::StrFormat(
587 "2BPP graphics sheet %u offset %u exceeds ROM size %zu", sheet_id,
588 offset, rom.
size()));
594 if (!decomp_result.ok()) {
595 return decomp_result.status();
598 for (
const auto& each_pixel : converted_sheet) {
599 sheet.push_back(each_pixel);
611 constexpr uint32_t kFontDataSize = 0x4000;
613 auto font_data_result =
615 if (!font_data_result.ok()) {
616 return font_data_result.status();
634 [[maybe_unused]]
Rom& rom,
635 [[maybe_unused]]
const std::array<gfx::Bitmap, kNumGfxSheets>& sheets) {
638 LOG_INFO(
"SaveAllGraphicsData",
"Graphics save not yet fully implemented");
639 return absl::OkStatus();
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::StatusOr< std::vector< uint8_t > > ReadByteVector(uint32_t offset, uint32_t length) const
absl::Status WriteByte(int addr, uint8_t value)
absl::StatusOr< uint8_t > ReadByte(int offset) const
const auto & vector() const
absl::StatusOr< uint16_t > ReadWord(int offset) const
static RomSettings & Get()
uint32_t GetAddressOr(const std::string &key, uint32_t default_value) const
Represents a bitmap image optimized for SNES ROM hacking.
void Create(int width, int height, int depth, std::span< uint8_t > data)
Create a bitmap with the given dimensions and data.
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index, int length=7)
Set the palette with a transparent color.
constexpr bool is_modified() const
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
static absl::Status LoadFromRom(Rom *rom, PitDamageTable *out)
#define LOG_DEBUG(category, format,...)
#define LOG_INFO(category, format,...)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
constexpr char kOverworldGfxPtr3[]
constexpr char kOverworldGfxPtr1[]
constexpr char kOverworldGfxPtr2[]
absl::StatusOr< std::vector< uint8_t > > DecompressV2(const uint8_t *data, int offset, int size, int mode, size_t rom_size)
Decompresses a buffer of data using the LC_LZ2 algorithm.
constexpr int kTilesheetHeight
constexpr int kTilesheetWidth
constexpr int kTilesheetDepth
std::vector< uint8_t > SnesTo8bppSheet(std::span< const uint8_t > sheet, int bpp, int num_sheets)
absl::Status LoadAllPalettes(const std::vector< uint8_t > &rom_data, PaletteGroupMap &groups)
Loads all the palettes for the game.
void ApplyDefaultPalette(gfx::Bitmap &bitmap, const gfx::SnesPalette &palette)
PaletteSlice GetDefaultPaletteSlice(const gfx::SnesPalette &palette)
absl::Status SaveGfxGroups(Rom &rom, const GameData &data)
absl::StatusOr< std::vector< uint8_t > > Load2BppGraphics(const Rom &rom)
Loads 2BPP graphics sheets from ROM.
constexpr uint16_t kLinkGfxLength
constexpr uint32_t kFontSpriteLocation
absl::StatusOr< std::array< gfx::Bitmap, kNumLinkSheets > > LoadLinkGraphics(const Rom &rom)
Loads Link's graphics sheets from ROM.
absl::StatusOr< gfx::Bitmap > LoadFontGraphics(const Rom &rom)
Loads font graphics from ROM.
constexpr uint32_t kNumRoomBlocksets
constexpr uint32_t kUncompressedSheetSize
absl::Status LoadGameData(Rom &rom, GameData &data, const LoadOptions &options)
Loads all Zelda3-specific game data from a generic ROM.
absl::Status LoadMetadata(const Rom &rom, GameData &data)
constexpr uint32_t kNumPalettesets
absl::Status SaveAllGraphicsData(Rom &rom, const std::array< gfx::Bitmap, kNumGfxSheets > &sheets)
Saves all graphics sheets back to ROM.
constexpr uint32_t kLinkGfxOffset
constexpr uint32_t kNumMainBlocksets
constexpr uint32_t kNumGfxSheets
constexpr uint32_t kEntranceGfxGroup
void ProcessSheetBitmap(GameData &data, uint32_t i, const SheetLoadResult &result)
constexpr uint32_t kNumSpritesets
constexpr uint32_t kNumLinkSheets
absl::Status LoadPalettes(const Rom &rom, GameData &data)
absl::Status SaveGameData(Rom &rom, GameData &data)
Saves modified game data back to the ROM.
absl::Status LoadGraphics(Rom &rom, GameData &data)
uint32_t GetGraphicsAddress(const uint8_t *data, uint8_t addr, uint32_t ptr1, uint32_t ptr2, uint32_t ptr3, size_t rom_size)
Gets the graphics address for a sheet index.
absl::Status LoadGfxGroups(Rom &rom, GameData &data)
constexpr int kGfxGroupsPointer
SheetLoadResult LoadSheetRaw(const Rom &rom, uint32_t i, uint32_t ptr1, uint32_t ptr2, uint32_t ptr3)
int AddressFromBytes(uint8_t bank, uint8_t high, uint8_t low) noexcept
uint32_t SnesToPc(uint32_t addr) noexcept
#define RETURN_IF_ERROR(expr)
absl::Status for_each(Func &&func)
PaletteGroup sprites_aux1
PaletteGroup dungeon_main
Represents a group of palettes.
auto palette(int i) const
auto mutable_palette(int i)
std::array< std::array< uint8_t, 4 >, kNumSpritesets > spriteset_ids
std::array< std::array< uint8_t, 4 >, kNumRoomBlocksets > room_blockset_ids
std::array< std::array< uint8_t, 4 >, kNumPalettesets > paletteset_ids
gfx::PaletteGroupMap palette_groups
GraphicsLoadDiagnostics diagnostics
PitDamageTable pit_damage_table
std::array< gfx::Bitmap, kNumGfxSheets > gfx_bitmaps
std::array< std::array< uint8_t, 8 >, kNumMainBlocksets > main_blockset_ids
std::array< std::vector< uint8_t >, kNumGfxSheets > raw_gfx_sheets
std::vector< uint8_t > graphics_buffer
std::vector< uint8_t > data
bool decompression_succeeded