3#include "absl/strings/str_format.h"
25 return (bank << 16) | (high << 8) | low;
30 return (snes_addr & 0x7FFF) | ((snes_addr & 0x7F0000) >> 1);
40 const int palette_size =
static_cast<int>(palette.
size());
41 if (palette_size <= 0) {
45 const bool has_explicit_transparent =
46 palette_size >= 16 && (palette_size % 16 == 0);
48 slice.
offset = has_explicit_transparent ? 1 : 0;
49 slice.
length = has_explicit_transparent ? 15 : std::min(palette_size, 15);
56 slice.
length = std::min(palette_size, 15);
112 uint32_t ptr2, uint32_t ptr3,
size_t rom_size) {
113 if (ptr1 > UINT32_MAX - addr || ptr1 + addr >= rom_size ||
114 ptr2 > UINT32_MAX - addr || ptr2 + addr >= rom_size ||
115 ptr3 > UINT32_MAX - addr || ptr3 + addr >= rom_size) {
116 return static_cast<uint32_t
>(rom_size);
144 if (rom.
size() < 1048576 * 2) {
149 return absl::OkStatus();
158 for (
size_t i = 0; i < group.
size(); ++i) {
160 for (
size_t j = 0; j < palette->size(); ++j) {
163 RETURN_IF_ERROR(rom.WriteColor(
164 gfx::GetPaletteAddress(group.name(), i, j), color));
165 color.set_modified(false);
169 return absl::OkStatus();
178 return absl::OkStatus();
182 constexpr uint32_t kTitleStringOffset = 0x7FC0;
183 constexpr uint32_t kTitleStringLength = 20;
185 if (rom.
size() < kTitleStringOffset + kTitleStringLength) {
186 return absl::OutOfRangeError(
"ROM too small for metadata");
190 if (kTitleStringOffset + 0x19 < rom.
size()) {
192 uint8_t version_byte = rom.
data()[kTitleStringOffset + 0x19];
194 (version_byte == 0) ? zelda3_version::JP : zelda3_version::US;
197 auto title_bytes = rom.
ReadByteVector(kTitleStringOffset, kTitleStringLength);
198 if (title_bytes.ok()) {
199 data.
title.assign(title_bytes->begin(), title_bytes->end());
202 return absl::OkStatus();
207 const std::vector<uint8_t>& rom_vec = rom.
vector();
212 if (kVersionConstantsMap.find(data.
version) == kVersionConstantsMap.end()) {
213 return absl::FailedPreconditionError(
"Unsupported ROM version");
216 auto version_constants = kVersionConstantsMap.at(data.
version);
220 if (main_ptr_res.ok()) {
221 uint32_t main_ptr =
SnesToPc(*main_ptr_res);
223 for (
int j = 0; j < 8; j++) {
224 auto val = rom.
ReadByte(main_ptr + (i * 8) + j);
233 for (
int j = 0; j < 4; j++) {
242 for (
int j = 0; j < 4; j++) {
244 rom.
ReadByte(version_constants.kSpriteBlocksetPointer + (i * 4) + j);
252 for (
int j = 0; j < 4; j++) {
254 rom.
ReadByte(version_constants.kDungeonPalettesGroups + (i * 4) + j);
260 return absl::OkStatus();
264 auto version_constants = kVersionConstantsMap.at(data.
version);
271 for (
int j = 0; j < 8; j++) {
279 for (
int j = 0; j < 4; j++) {
287 for (
int j = 0; j < 4; j++) {
289 rom.
WriteByte(version_constants.kSpriteBlocksetPointer + (i * 4) + j,
296 for (
int j = 0; j < 4; j++) {
298 rom.
WriteByte(version_constants.kDungeonPalettesGroups + (i * 4) + j,
303 return absl::OkStatus();
340 if (kVersionConstantsMap.find(data.
version) == kVersionConstantsMap.end()) {
341 return absl::FailedPreconditionError(
342 "Unsupported ROM version for graphics");
344 auto version_constants = kVersionConstantsMap.at(data.
version);
349 auto loading_handle =
350 app::platform::WasmLoadingManager::BeginLoading(
"Loading Graphics");
356 diag.ptr1_loc = version_constants.kOverworldGfxPtr1;
357 diag.ptr2_loc = version_constants.kOverworldGfxPtr2;
358 diag.ptr3_loc = version_constants.kOverworldGfxPtr3;
362 app::platform::WasmLoadingManager::UpdateProgress(
366 diag.sheets[i].index = i;
367 std::vector<uint8_t> sheet;
372 if (i >= 115 && i <= 126) {
373 diag.sheets[i].is_compressed =
false;
376 version_constants.kOverworldGfxPtr2,
377 version_constants.kOverworldGfxPtr3, rom.
size());
378 diag.sheets[i].pc_offset = offset;
384 diag.sheets[i].decompression_succeeded =
true;
388 diag.sheets[i].decompression_succeeded =
false;
392 else if (i == 113 || i == 114 || i >= 218) {
393 diag.sheets[i].is_compressed =
true;
398 diag.sheets[i].is_compressed =
true;
401 version_constants.kOverworldGfxPtr2,
402 version_constants.kOverworldGfxPtr3, rom.
size());
403 diag.sheets[i].pc_offset = offset;
405 if (offset < rom.
size()) {
412 if (decomp_res.ok()) {
414 diag.sheets[i].decompression_succeeded =
true;
417 diag.sheets[i].decompression_succeeded =
false;
425 if (converted_sheet.size() != 4096)
426 converted_sheet.resize(4096, 0);
443 if (!default_palette.
empty()) {
444 ApplyDefaultPalette(data.
gfx_bitmaps[i], default_palette);
447 std::vector<gfx::SnesColor> grayscale;
448 for (
int color_idx = 0; color_idx < 16; ++color_idx) {
449 float val = color_idx / 15.0f;
450 grayscale.emplace_back(ImVec4(val, val, val, 1.0f));
453 if (!grayscale.empty()) {
454 grayscale[0].set_transparent(
true);
460 std::vector<gfx::SnesColor> grayscale;
461 for (
int color_idx = 0; color_idx < 16; ++color_idx) {
462 float val = color_idx / 15.0f;
463 grayscale.emplace_back(ImVec4(val, val, val, 1.0f));
466 if (!grayscale.empty()) {
467 grayscale[0].set_transparent(
true);
477 std::vector<uint8_t> placeholder(4096, 0);
488 app::platform::WasmLoadingManager::EndLoading(loading_handle);
491 return absl::OkStatus();
500 std::array<gfx::Bitmap, kNumLinkSheets> link_graphics;
502 auto link_sheet_data_result =
505 if (!link_sheet_data_result.ok()) {
506 return link_sheet_data_result.status();
508 auto link_sheet_8bpp =
510 if (link_sheet_8bpp.size() != 4096)
511 link_sheet_8bpp.resize(4096, 0);
517 return link_graphics;
525 std::vector<uint8_t> sheet;
526 const uint8_t sheets[] = {0x71, 0x72, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE};
529 auto version_constants = kVersionConstantsMap.at(zelda3_version::US);
531 for (
const auto& sheet_id : sheets) {
533 rom.
data(), sheet_id, version_constants.kOverworldGfxPtr1,
534 version_constants.kOverworldGfxPtr2,
535 version_constants.kOverworldGfxPtr3, rom.
size());
537 if (offset >= rom.
size()) {
538 return absl::OutOfRangeError(absl::StrFormat(
539 "2BPP graphics sheet %u offset %u exceeds ROM size %zu", sheet_id,
540 offset, rom.
size()));
546 if (!decomp_result.ok()) {
547 return decomp_result.status();
550 for (
const auto& each_pixel : converted_sheet) {
551 sheet.push_back(each_pixel);
563 constexpr uint32_t kFontDataSize = 0x4000;
565 auto font_data_result =
567 if (!font_data_result.ok()) {
568 return font_data_result.status();
586 [[maybe_unused]]
Rom& rom,
587 [[maybe_unused]]
const std::array<gfx::Bitmap, kNumGfxSheets>& sheets) {
590 LOG_INFO(
"SaveAllGraphicsData",
"Graphics save not yet fully implemented");
591 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.
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_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.
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
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