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) {
150 return absl::OkStatus();
159 for (
size_t i = 0; i < group.
size(); ++i) {
161 for (
size_t j = 0; j < palette->size(); ++j) {
164 RETURN_IF_ERROR(rom.WriteColor(
165 gfx::GetPaletteAddress(group.name(), i, j), color));
166 color.set_modified(false);
170 return absl::OkStatus();
179 return absl::OkStatus();
183 constexpr uint32_t kTitleStringOffset = 0x7FC0;
184 constexpr uint32_t kTitleStringLength = 20;
186 if (rom.
size() < kTitleStringOffset + kTitleStringLength) {
187 return absl::OutOfRangeError(
"ROM too small for metadata");
191 if (kTitleStringOffset + 0x19 < rom.
size()) {
193 uint8_t version_byte = rom.
data()[kTitleStringOffset + 0x19];
195 (version_byte == 0) ? zelda3_version::JP : zelda3_version::US;
198 auto title_bytes = rom.
ReadByteVector(kTitleStringOffset, kTitleStringLength);
199 if (title_bytes.ok()) {
200 data.
title.assign(title_bytes->begin(), title_bytes->end());
203 return absl::OkStatus();
208 const std::vector<uint8_t>& rom_vec = rom.
vector();
213 if (kVersionConstantsMap.find(data.
version) == kVersionConstantsMap.end()) {
214 return absl::FailedPreconditionError(
"Unsupported ROM version");
217 auto version_constants = kVersionConstantsMap.at(data.
version);
221 if (main_ptr_res.ok()) {
222 uint32_t main_ptr =
SnesToPc(*main_ptr_res);
224 for (
int j = 0; j < 8; j++) {
225 auto val = rom.
ReadByte(main_ptr + (i * 8) + j);
234 for (
int j = 0; j < 4; j++) {
243 for (
int j = 0; j < 4; j++) {
245 rom.
ReadByte(version_constants.kSpriteBlocksetPointer + (i * 4) + j);
253 for (
int j = 0; j < 4; j++) {
255 rom.
ReadByte(version_constants.kDungeonPalettesGroups + (i * 4) + j);
261 return absl::OkStatus();
265 auto version_constants = kVersionConstantsMap.at(data.
version);
272 for (
int j = 0; j < 8; j++) {
280 for (
int j = 0; j < 4; j++) {
288 for (
int j = 0; j < 4; j++) {
290 rom.
WriteByte(version_constants.kSpriteBlocksetPointer + (i * 4) + j,
297 for (
int j = 0; j < 4; j++) {
299 rom.
WriteByte(version_constants.kDungeonPalettesGroups + (i * 4) + j,
304 return absl::OkStatus();
349 uint32_t ptr2, uint32_t ptr3) {
354 if (i >= 115 && i <= 126) {
362 result.
data = *read_res;
370 else if (i == 113 || i == 114 || i >= 218) {
383 if (decomp_res.ok()) {
384 result.
data = *decomp_res;
398 if (converted_sheet.size() != 4096)
399 converted_sheet.resize(4096, 0);
416 if (!default_palette.
empty()) {
417 ApplyDefaultPalette(data.
gfx_bitmaps[i], default_palette);
420 std::vector<gfx::SnesColor> grayscale;
421 for (
int color_idx = 0; color_idx < 16; ++color_idx) {
422 float val = color_idx / 15.0f;
423 grayscale.emplace_back(ImVec4(val, val, val, 1.0f));
425 if (!grayscale.empty()) {
426 grayscale[0].set_transparent(
true);
432 std::vector<gfx::SnesColor> grayscale;
433 for (
int color_idx = 0; color_idx < 16; ++color_idx) {
434 float val = color_idx / 15.0f;
435 grayscale.emplace_back(ImVec4(val, val, val, 1.0f));
437 if (!grayscale.empty()) {
438 grayscale[0].set_transparent(
true);
448 std::vector<uint8_t> placeholder(4096, 0);
457 if (kVersionConstantsMap.find(data.
version) == kVersionConstantsMap.end()) {
458 return absl::FailedPreconditionError(
459 "Unsupported ROM version for graphics");
461 auto version_constants = kVersionConstantsMap.at(data.
version);
464 version_constants.kOverworldGfxPtr1);
467 version_constants.kOverworldGfxPtr2);
470 version_constants.kOverworldGfxPtr3);
475 auto loading_handle =
476 app::platform::WasmLoadingManager::BeginLoading(
"Loading Graphics");
482 diag.ptr1_loc = gfx_ptr1;
483 diag.ptr2_loc = gfx_ptr2;
484 diag.ptr3_loc = gfx_ptr3;
490 app::platform::WasmLoadingManager::UpdateProgress(
495 auto result =
LoadSheetRaw(rom, i, gfx_ptr1, gfx_ptr2, gfx_ptr3);
498 auto& sd = diag.sheets[i];
500 sd.is_compressed = result.is_compressed;
501 sd.pc_offset = result.pc_offset;
502 sd.decompression_succeeded = result.decompression_succeeded;
503 sd.actual_decomp_size = result.data.size();
504 if (!result.data.empty()) {
505 size_t count = std::min<size_t>(result.data.size(), 8);
506 sd.first_bytes.assign(result.data.begin(), result.data.begin() + count);
508 if (result.is_compressed && !result.is_bpp3) {
509 sd.decomp_size_param = 0x800;
515 LOG_DEBUG(
"Graphics",
"Sheet %d: offset=0x%06X, size=%zu, %s",
516 i, result.pc_offset, result.data.size(),
517 result.decompression_succeeded ?
"OK" :
"FAILED");
525 app::platform::WasmLoadingManager::EndLoading(loading_handle);
528 return absl::OkStatus();
537 std::array<gfx::Bitmap, kNumLinkSheets> link_graphics;
539 auto link_sheet_data_result =
542 if (!link_sheet_data_result.ok()) {
543 return link_sheet_data_result.status();
545 auto link_sheet_8bpp =
547 if (link_sheet_8bpp.size() != 4096)
548 link_sheet_8bpp.resize(4096, 0);
554 return link_graphics;
562 std::vector<uint8_t> sheet;
563 const uint8_t sheets[] = {0x71, 0x72, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE};
566 auto version_constants = kVersionConstantsMap.at(zelda3_version::US);
569 version_constants.kOverworldGfxPtr1);
572 version_constants.kOverworldGfxPtr2);
575 version_constants.kOverworldGfxPtr3);
577 for (
const auto& sheet_id : sheets) {
579 gfx_ptr3, rom.
size());
581 if (offset >= rom.
size()) {
582 return absl::OutOfRangeError(absl::StrFormat(
583 "2BPP graphics sheet %u offset %u exceeds ROM size %zu", sheet_id,
584 offset, rom.
size()));
590 if (!decomp_result.ok()) {
591 return decomp_result.status();
594 for (
const auto& each_pixel : converted_sheet) {
595 sheet.push_back(each_pixel);
607 constexpr uint32_t kFontDataSize = 0x4000;
609 auto font_data_result =
611 if (!font_data_result.ok()) {
612 return font_data_result.status();
630 [[maybe_unused]]
Rom& rom,
631 [[maybe_unused]]
const std::array<gfx::Bitmap, kNumGfxSheets>& sheets) {
634 LOG_INFO(
"SaveAllGraphicsData",
"Graphics save not yet fully implemented");
635 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).
#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
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