3#include "absl/strings/str_format.h"
23 return (bank << 16) | (high << 8) | low;
28 return (snes_addr & 0x7FFF) | ((snes_addr & 0x7F0000) >> 1);
70 uint32_t ptr2, uint32_t ptr3,
size_t rom_size) {
71 if (ptr1 > UINT32_MAX - addr || ptr1 + addr >= rom_size ||
72 ptr2 > UINT32_MAX - addr || ptr2 + addr >= rom_size ||
73 ptr3 > UINT32_MAX - addr || ptr3 + addr >= rom_size) {
74 return static_cast<uint32_t
>(rom_size);
100 if (rom.
size() < 1048576 * 2) {
105 return absl::OkStatus();
113 for (
size_t i = 0; i < group.
size(); ++i) {
115 for (
size_t j = 0; j < palette->size(); ++j) {
118 RETURN_IF_ERROR(rom.WriteColor(
119 gfx::GetPaletteAddress(group.name(), i, j), color));
120 color.set_modified(false);
124 return absl::OkStatus();
133 return absl::OkStatus();
137 constexpr uint32_t kTitleStringOffset = 0x7FC0;
138 constexpr uint32_t kTitleStringLength = 20;
140 if (rom.
size() < kTitleStringOffset + kTitleStringLength) {
141 return absl::OutOfRangeError(
"ROM too small for metadata");
145 if (kTitleStringOffset + 0x19 < rom.
size()) {
147 uint8_t version_byte = rom.
data()[kTitleStringOffset + 0x19];
148 data.
version = (version_byte == 0) ? zelda3_version::JP : zelda3_version::US;
151 auto title_bytes = rom.
ReadByteVector(kTitleStringOffset, kTitleStringLength);
152 if (title_bytes.ok()) {
153 data.
title.assign(title_bytes->begin(), title_bytes->end());
156 return absl::OkStatus();
161 const std::vector<uint8_t>& rom_vec = rom.
vector();
166 if (kVersionConstantsMap.find(data.
version) == kVersionConstantsMap.end()) {
167 return absl::FailedPreconditionError(
"Unsupported ROM version");
170 auto version_constants = kVersionConstantsMap.at(data.
version);
174 if (main_ptr_res.ok()) {
175 uint32_t main_ptr =
SnesToPc(*main_ptr_res);
177 for (
int j = 0; j < 8; j++) {
178 auto val = rom.
ReadByte(main_ptr + (i * 8) + j);
186 for (
int j = 0; j < 4; j++) {
194 for (
int j = 0; j < 4; j++) {
195 auto val = rom.
ReadByte(version_constants.kSpriteBlocksetPointer + (i * 4) + j);
202 for (
int j = 0; j < 4; j++) {
203 auto val = rom.
ReadByte(version_constants.kDungeonPalettesGroups + (i * 4) + j);
208 return absl::OkStatus();
212 auto version_constants = kVersionConstantsMap.at(data.
version);
219 for (
int j = 0; j < 8; j++) {
227 for (
int j = 0; j < 4; j++) {
235 for (
int j = 0; j < 4; j++) {
243 for (
int j = 0; j < 4; j++) {
249 return absl::OkStatus();
286 if (kVersionConstantsMap.find(data.
version) == kVersionConstantsMap.end()) {
287 return absl::FailedPreconditionError(
"Unsupported ROM version for graphics");
289 auto version_constants = kVersionConstantsMap.at(data.
version);
294 auto loading_handle = app::platform::WasmLoadingManager::BeginLoading(
"Loading Graphics");
300 diag.ptr1_loc = version_constants.kOverworldGfxPtr1;
301 diag.ptr2_loc = version_constants.kOverworldGfxPtr2;
302 diag.ptr3_loc = version_constants.kOverworldGfxPtr3;
306 app::platform::WasmLoadingManager::UpdateProgress(loading_handle,
static_cast<float>(i) /
kNumGfxSheets);
309 diag.sheets[i].index = i;
310 std::vector<uint8_t> sheet;
315 if (i >= 115 && i <= 126) {
316 diag.sheets[i].is_compressed =
false;
317 offset =
GetGraphicsAddress(rom.
data(), i, version_constants.kOverworldGfxPtr1, version_constants.kOverworldGfxPtr2, version_constants.kOverworldGfxPtr3, rom.
size());
318 diag.sheets[i].pc_offset = offset;
323 diag.sheets[i].decompression_succeeded =
true;
327 diag.sheets[i].decompression_succeeded =
false;
331 else if (i == 113 || i == 114 || i >= 218) {
332 diag.sheets[i].is_compressed =
true;
337 diag.sheets[i].is_compressed =
true;
338 offset =
GetGraphicsAddress(rom.
data(), i, version_constants.kOverworldGfxPtr1, version_constants.kOverworldGfxPtr2, version_constants.kOverworldGfxPtr3, rom.
size());
339 diag.sheets[i].pc_offset = offset;
341 if (offset < rom.
size()) {
347 if (decomp_res.ok()) {
349 diag.sheets[i].decompression_succeeded =
true;
352 diag.sheets[i].decompression_succeeded =
false;
360 if (converted_sheet.size() != 4096) converted_sheet.resize(4096, 0);
377 if (!default_palette.
empty()) {
381 std::vector<gfx::SnesColor> grayscale;
382 for (
int color_idx = 0; color_idx < 16; ++color_idx) {
383 float val = color_idx / 15.0f;
384 grayscale.emplace_back(ImVec4(val, val, val, 1.0f));
387 if (!grayscale.empty()) {
388 grayscale[0].set_transparent(
true);
394 std::vector<gfx::SnesColor> grayscale;
395 for (
int color_idx = 0; color_idx < 16; ++color_idx) {
396 float val = color_idx / 15.0f;
397 grayscale.emplace_back(ImVec4(val, val, val, 1.0f));
400 if (!grayscale.empty()) {
401 grayscale[0].set_transparent(
true);
411 std::vector<uint8_t> placeholder(4096, 0);
422 app::platform::WasmLoadingManager::EndLoading(loading_handle);
425 return absl::OkStatus();
434 std::array<gfx::Bitmap, kNumLinkSheets> link_graphics;
436 auto link_sheet_data_result =
439 if (!link_sheet_data_result.ok()) {
440 return link_sheet_data_result.status();
443 if (link_sheet_8bpp.size() != 4096) link_sheet_8bpp.resize(4096, 0);
449 return link_graphics;
457 std::vector<uint8_t> sheet;
458 const uint8_t sheets[] = {0x71, 0x72, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE};
461 auto version_constants = kVersionConstantsMap.at(zelda3_version::US);
463 for (
const auto& sheet_id : sheets) {
465 version_constants.kOverworldGfxPtr1,
466 version_constants.kOverworldGfxPtr2,
467 version_constants.kOverworldGfxPtr3,
470 if (offset >= rom.
size()) {
471 return absl::OutOfRangeError(
472 absl::StrFormat(
"2BPP graphics sheet %u offset %u exceeds ROM size %zu",
473 sheet_id, offset, rom.
size()));
478 if (!decomp_result.ok()) {
479 return decomp_result.status();
482 for (
const auto& each_pixel : converted_sheet) {
483 sheet.push_back(each_pixel);
495 constexpr uint32_t kFontDataSize = 0x4000;
498 if (!font_data_result.ok()) {
499 return font_data_result.status();
517 [[maybe_unused]]
Rom& rom, [[maybe_unused]]
const std::array<gfx::Bitmap, kNumGfxSheets>& sheets) {
520 LOG_INFO(
"SaveAllGraphicsData",
"Graphics save not yet fully implemented");
521 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)
const auto & vector() const
absl::StatusOr< uint16_t > ReadWord(int offset)
absl::StatusOr< uint8_t > ReadByte(int offset)
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.
constexpr bool is_modified() const
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
#define LOG_INFO(category, format,...)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
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< 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.
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
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
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