Refactor ROM class

This commit is contained in:
scawful
2024-04-14 11:57:59 -05:00
parent bfdffce310
commit 9604ce3724
2 changed files with 98 additions and 96 deletions

View File

@@ -220,7 +220,8 @@ absl::Status ROM::LoadLinkGraphics() {
auto link_sheet_8bpp = gfx::SnesTo8bppSheet(link_sheet_3bpp, /*bpp=*/3);
link_graphics_[i].Create(core::kTilesheetWidth, core::kTilesheetHeight,
core::kTilesheetDepth, link_sheet_8bpp);
RETURN_IF_ERROR(link_graphics_[i].ApplyPaletteWithTransparent(link_palette_, 0));
RETURN_IF_ERROR(
link_graphics_[i].ApplyPaletteWithTransparent(link_palette_, 0));
RenderBitmap(&link_graphics_[i]);
}
@@ -228,6 +229,7 @@ absl::Status ROM::LoadLinkGraphics() {
}
absl::Status ROM::LoadAllGraphicsData() {
constexpr uint32_t kNumGfxSheets = 223;
Bytes sheet;
bool bpp3 = false;
@@ -255,11 +257,11 @@ absl::Status ROM::LoadAllGraphicsData() {
core::kTilesheetDepth);
if (i > 115) {
// Apply sprites palette
graphics_manager_[i]->ApplyPaletteWithTransparent(
palette_groups_["global_sprites"][0], 0);
RETURN_IF_ERROR(graphics_manager_[i]->ApplyPaletteWithTransparent(
palette_groups_["global_sprites"][0], 0));
} else {
graphics_manager_[i]->ApplyPaletteWithTransparent(
palette_groups_["dungeon_main"][0], 0);
RETURN_IF_ERROR(graphics_manager_[i]->ApplyPaletteWithTransparent(
palette_groups_["dungeon_main"][0], 0));
}
graphics_manager_[i]->CreateTexture(renderer_);
}
@@ -341,6 +343,8 @@ absl::Status ROM::LoadFromFile(const absl::string_view& filename,
// Load Zelda 3 specific data if requested
if (z3_load) {
// Copy ROM title
constexpr uint32_t kTitleStringOffset = 0x7FC0;
constexpr uint32_t kTitleStringLength = 20;
memcpy(title_, rom_data_.data() + kTitleStringOffset, kTitleStringLength);
if (rom_data_[kTitleStringOffset + 0x19] == 0) {
version_ = Z3_Version::JP;

View File

@@ -44,13 +44,15 @@ using PaletteGroupMap = std::unordered_map<std::string, gfx::PaletteGroup>;
// Define an enum class for the different versions of the game
enum class Z3_Version {
US = 1,
JP = 2,
SD = 3,
RANDO = 4,
US = 1, // US version
JP = 2, // JP version
SD = 3, // Super Donkey Proto (Experimental)
RANDO = 4, // Randomizer (Unimplemented)
};
// Define a struct to hold the version-specific constants
/**
* @brief A struct to hold version constants for each version of the game.
*/
struct VersionConstants {
uint32_t kGfxAnimatedPointer;
uint32_t kOverworldGfxGroups1;
@@ -72,7 +74,9 @@ struct VersionConstants {
uint32_t kDungeonPalettesGroups;
};
// Define a map to hold the version constants for each version
/**
* @brief A map of version constants for each version of the game.
*/
static const std::map<Z3_Version, VersionConstants> kVersionConstantsMap = {
{Z3_Version::US,
{
@@ -117,91 +121,16 @@ static const std::map<Z3_Version, VersionConstants> kVersionConstantsMap = {
0x67DD0, // kDungeonPalettesGroups
}}};
// Define some constants used throughout the ROM class
constexpr uint32_t kOverworldGraphicsPos1 = 0x4F80;
constexpr uint32_t kOverworldGraphicsPos2 = 0x505F;
constexpr uint32_t kOverworldGraphicsPos3 = 0x513E;
constexpr uint32_t kTile32Num = 4432;
constexpr uint32_t kTitleStringOffset = 0x7FC0;
constexpr uint32_t kTitleStringLength = 20;
constexpr uint32_t kNumGfxSheets = 223;
constexpr uint32_t kNormalGfxSpaceStart = 0x87000;
constexpr uint32_t kNormalGfxSpaceEnd = 0xC4200;
constexpr uint32_t kLinkSpriteLocation = 0x80000;
constexpr uint32_t kFontSpriteLocation = 0x70000;
constexpr uint32_t gfx_groups_pointer = 0x6237;
struct WriteAction {
int address;
std::variant<int, uint8_t, uint16_t, short, std::vector<uint8_t>,
gfx::SnesColor, std::vector<gfx::SnesColor>>
value;
};
constexpr uint32_t kGfxGroupsPointer = 0x6237;
/**
* @brief The ROM class is used to load, save, and modify ROM data.
*/
class ROM : public core::ExperimentFlags {
public:
template <typename... Args>
absl::Status RunTransaction(Args... args) {
absl::Status status;
// Fold expression to apply the Write function on each argument
((status = WriteHelper(args)), ...);
return status;
}
absl::Status WriteHelper(const WriteAction& action) {
if (std::holds_alternative<uint8_t>(action.value)) {
return Write(action.address, std::get<uint8_t>(action.value));
} else if (std::holds_alternative<uint16_t>(action.value) ||
std::holds_alternative<short>(action.value)) {
return WriteShort(action.address, std::get<uint16_t>(action.value));
} else if (std::holds_alternative<std::vector<uint8_t>>(action.value)) {
return WriteVector(action.address,
std::get<std::vector<uint8_t>>(action.value));
} else if (std::holds_alternative<gfx::SnesColor>(action.value)) {
return WriteColor(action.address, std::get<gfx::SnesColor>(action.value));
} else if (std::holds_alternative<std::vector<gfx::SnesColor>>(
action.value)) {
return absl::UnimplementedError(
"WriteHelper: std::vector<gfx::SnesColor>");
}
auto error_message = absl::StrFormat("Invalid write argument type: %s",
typeid(action.value).name());
throw std::runtime_error(error_message);
return absl::InvalidArgumentError(error_message);
}
template <typename T, typename... Args>
absl::Status ReadTransaction(T& var, int address, Args&&... args) {
absl::Status status = ReadHelper<T>(var, address);
if (!status.ok()) {
return status;
}
if constexpr (sizeof...(args) > 0) {
status = ReadTransaction(std::forward<Args>(args)...);
}
return status;
}
template <typename T>
absl::Status ReadHelper(T& var, int address) {
if constexpr (std::is_same_v<T, uint8_t>) {
ASSIGN_OR_RETURN(auto result, ReadByte(address));
var = result;
} else if constexpr (std::is_same_v<T, uint16_t>) {
ASSIGN_OR_RETURN(auto result, ReadWord(address));
var = result;
} else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
ASSIGN_OR_RETURN(auto result, ReadByteVector(address, var.size()));
var = result;
}
return absl::OkStatus();
}
/**
* @brief Loads 2bpp graphics from ROM data.
*
@@ -225,11 +154,14 @@ class ROM : public core::ExperimentFlags {
* data is converted to 8 BPP and stored in a bitmap.
*
* The graphics sheets are divided into the following ranges:
* 0-112 -> compressed 3bpp bgr -> (decompressed each) 0x600 chars
* 113-114 -> compressed 2bpp -> (decompressed each) 0x800 chars
* 115-126 -> uncompressed 3bpp sprites -> (each) 0x600 chars
* 127-217 -> compressed 3bpp sprites -> (decompressed each) 0x600 chars
* 218-222 -> compressed 2bpp -> (decompressed each) 0x800 chars
*
* | Range | Compression Type | Decompressed Size | Number of Chars |
* |---------|------------------|------------------|-----------------|
* | 0-112 | Compressed 3bpp BGR | 0x600 chars | Decompressed each |
* | 113-114 | Compressed 2bpp | 0x800 chars | Decompressed each |
* | 115-126 | Uncompressed 3bpp sprites | 0x600 chars | Each |
* | 127-217 | Compressed 3bpp sprites | 0x600 chars | Decompressed each |
* | 218-222 | Compressed 2bpp | 0x800 chars | Decompressed each |
*
*/
absl::Status LoadAllGraphicsData();
@@ -468,6 +400,28 @@ class ROM : public core::ExperimentFlags {
return WriteShort(address, bgr);
}
template <typename... Args>
absl::Status WriteTransaction(Args... args) {
absl::Status status;
// Fold expression to apply the Write function on each argument
((status = WriteHelper(args)), ...);
return status;
}
template <typename T, typename... Args>
absl::Status ReadTransaction(T& var, int address, Args&&... args) {
absl::Status status = ReadHelper<T>(var, address);
if (!status.ok()) {
return status;
}
if constexpr (sizeof...(args) > 0) {
status = ReadTransaction(std::forward<Args>(args)...);
}
return status;
}
void Expand(int size) {
rom_data_.resize(size);
size_ = size;
@@ -596,8 +550,8 @@ class ROM : public core::ExperimentFlags {
spriteset_ids.resize(144, std::vector<uint8_t>(4));
paletteset_ids.resize(72, std::vector<uint8_t>(4));
int gfxPointer = (rom_data_[gfx_groups_pointer + 1] << 8) +
rom_data_[gfx_groups_pointer];
int gfxPointer =
(rom_data_[kGfxGroupsPointer + 1] << 8) + rom_data_[kGfxGroupsPointer];
gfxPointer = core::SnesToPc(gfxPointer);
for (int i = 0; i < 37; i++) {
@@ -629,8 +583,8 @@ class ROM : public core::ExperimentFlags {
}
bool SaveGroupsToROM() {
int gfxPointer = (rom_data_[gfx_groups_pointer + 1] << 8) +
rom_data_[gfx_groups_pointer];
int gfxPointer =
(rom_data_[kGfxGroupsPointer + 1] << 8) + rom_data_[kGfxGroupsPointer];
gfxPointer = core::SnesToPc(gfxPointer);
for (int i = 0; i < 37; i++) {
@@ -666,6 +620,50 @@ class ROM : public core::ExperimentFlags {
auto resource_label() { return &resource_label_manager_; }
private:
struct WriteAction {
int address;
std::variant<int, uint8_t, uint16_t, short, std::vector<uint8_t>,
gfx::SnesColor, std::vector<gfx::SnesColor>>
value;
};
absl::Status WriteHelper(const WriteAction& action) {
if (std::holds_alternative<uint8_t>(action.value)) {
return Write(action.address, std::get<uint8_t>(action.value));
} else if (std::holds_alternative<uint16_t>(action.value) ||
std::holds_alternative<short>(action.value)) {
return WriteShort(action.address, std::get<uint16_t>(action.value));
} else if (std::holds_alternative<std::vector<uint8_t>>(action.value)) {
return WriteVector(action.address,
std::get<std::vector<uint8_t>>(action.value));
} else if (std::holds_alternative<gfx::SnesColor>(action.value)) {
return WriteColor(action.address, std::get<gfx::SnesColor>(action.value));
} else if (std::holds_alternative<std::vector<gfx::SnesColor>>(
action.value)) {
return absl::UnimplementedError(
"WriteHelper: std::vector<gfx::SnesColor>");
}
auto error_message = absl::StrFormat("Invalid write argument type: %s",
typeid(action.value).name());
throw std::runtime_error(error_message);
return absl::InvalidArgumentError(error_message);
}
template <typename T>
absl::Status ReadHelper(T& var, int address) {
if constexpr (std::is_same_v<T, uint8_t>) {
ASSIGN_OR_RETURN(auto result, ReadByte(address));
var = result;
} else if constexpr (std::is_same_v<T, uint16_t>) {
ASSIGN_OR_RETURN(auto result, ReadWord(address));
var = result;
} else if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
ASSIGN_OR_RETURN(auto result, ReadByteVector(address, var.size()));
var = result;
}
return absl::OkStatus();
}
long size_ = 0;
bool is_loaded_ = false;
bool has_header_ = false;