yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
pit_damage_table.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <vector>
5
6#include "absl/strings/str_format.h"
7#include "rom/snes.h"
9
10namespace yaze {
11namespace zelda3 {
12namespace {
13
14int PitTableEntryCount(uint8_t max_offset_byte) {
15 return static_cast<int>(max_offset_byte) / 2 + 1;
16}
17
18int PitTableByteLength(uint8_t max_offset_byte) {
19 return max_offset_byte + 2;
20}
21
22} // namespace
23
25 if (!rom || !rom->is_loaded() || out == nullptr) {
26 return absl::InvalidArgumentError("ROM not loaded");
27 }
28 const auto& rom_data = rom->vector();
29 if (kPitCount < 0 || kPitCount >= static_cast<int>(rom_data.size()) ||
30 kPitPointer + 2 >= static_cast<int>(rom_data.size())) {
31 return absl::OutOfRangeError("Pit count/pointer out of range");
32 }
33
34 const uint8_t max_offset = rom_data[kPitCount];
35 const int entry_count = PitTableEntryCount(max_offset);
36 const int data_len = PitTableByteLength(max_offset);
37
38 const int pit_ptr_snes = (rom_data[kPitPointer + 2] << 16) |
39 (rom_data[kPitPointer + 1] << 8) |
40 rom_data[kPitPointer];
41 const int pit_data_pc = SnesToPc(pit_ptr_snes);
42 if (pit_data_pc < 0 ||
43 pit_data_pc + data_len > static_cast<int>(rom_data.size())) {
44 return absl::OutOfRangeError("Pit data region out of range");
45 }
46
47 std::vector<uint16_t> room_ids;
48 room_ids.reserve(entry_count);
49 for (int offset = 0; offset < data_len; offset += 2) {
50 const int addr = pit_data_pc + offset;
51 const uint16_t room_id = static_cast<uint16_t>(rom_data[addr]) |
52 (static_cast<uint16_t>(rom_data[addr + 1]) << 8);
53 room_ids.push_back(room_id);
54 }
55
56 out->room_ids_ = std::move(room_ids);
57 out->dirty_ = false;
58 return absl::OkStatus();
59}
60
61absl::Status PitDamageTable::SaveToRom(Rom* rom) const {
62 if (!rom || !rom->is_loaded()) {
63 return absl::InvalidArgumentError("ROM not loaded");
64 }
65 const auto& rom_data = rom->vector();
66 if (kPitCount < 0 || kPitCount >= static_cast<int>(rom_data.size()) ||
67 kPitPointer + 2 >= static_cast<int>(rom_data.size())) {
68 return absl::OutOfRangeError("Pit count/pointer out of range");
69 }
70
71 const uint8_t max_offset = rom_data[kPitCount];
72 const int expected_entries = PitTableEntryCount(max_offset);
73 const int data_len = PitTableByteLength(max_offset);
74 if (static_cast<int>(room_ids_.size()) != expected_entries) {
75 return absl::FailedPreconditionError(absl::StrFormat(
76 "PitDamageTable entry count %zu does not match ROM table capacity %d",
77 room_ids_.size(), expected_entries));
78 }
79 std::vector<bool> seen(kNumberOfRooms, false);
80 for (size_t index = 0; index < room_ids_.size(); ++index) {
81 const uint16_t room_id = room_ids_[index];
82 if (room_id >= kNumberOfRooms) {
83 return absl::InvalidArgumentError(absl::StrFormat(
84 "PitDamageTable room 0x%03X at index %zu is outside the dungeon "
85 "room range",
86 room_id, index));
87 }
88 if (seen[room_id]) {
89 return absl::AlreadyExistsError(absl::StrFormat(
90 "PitDamageTable room 0x%03X appears more than once", room_id));
91 }
92 seen[room_id] = true;
93 }
94
95 const int pit_ptr_snes = (rom_data[kPitPointer + 2] << 16) |
96 (rom_data[kPitPointer + 1] << 8) |
97 rom_data[kPitPointer];
98 const int pit_data_pc = SnesToPc(pit_ptr_snes);
99 if (pit_data_pc < 0 ||
100 pit_data_pc + data_len > static_cast<int>(rom_data.size())) {
101 return absl::OutOfRangeError("Pit data region out of range");
102 }
103
104 std::vector<uint8_t> encoded;
105 encoded.reserve(data_len);
106 for (uint16_t room_id : room_ids_) {
107 encoded.push_back(static_cast<uint8_t>(room_id & 0xFF));
108 encoded.push_back(static_cast<uint8_t>((room_id >> 8) & 0xFF));
109 }
110 return rom->WriteVector(pit_data_pc, encoded);
111}
112
113bool PitDamageTable::Contains(uint16_t room_id) const {
114 return std::find(room_ids_.begin(), room_ids_.end(), room_id) !=
115 room_ids_.end();
116}
117
118void PitDamageTable::SetRoomIds(std::vector<uint16_t> room_ids) {
119 room_ids_ = std::move(room_ids);
120 dirty_ = true;
121}
122
123absl::Status PitDamageTable::ReplaceRoomId(uint16_t old_room_id,
124 uint16_t new_room_id) {
125 if (new_room_id >= kNumberOfRooms) {
126 return absl::InvalidArgumentError(absl::StrFormat(
127 "Replacement room 0x%03X is outside the dungeon room range",
128 new_room_id));
129 }
130
131 auto old_it = std::find(room_ids_.begin(), room_ids_.end(), old_room_id);
132 if (old_it == room_ids_.end()) {
133 return absl::NotFoundError(absl::StrFormat(
134 "Room 0x%03X is not in RoomsWithPitDamage", old_room_id));
135 }
136
137 if (old_room_id == new_room_id) {
138 return absl::OkStatus();
139 }
140
141 if (Contains(new_room_id)) {
142 return absl::AlreadyExistsError(absl::StrFormat(
143 "Room 0x%03X is already in RoomsWithPitDamage", new_room_id));
144 }
145
146 *old_it = new_room_id;
147 dirty_ = true;
148 return absl::OkStatus();
149}
150
151} // namespace zelda3
152} // namespace yaze
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Definition rom.h:28
const auto & vector() const
Definition rom.h:155
absl::Status WriteVector(int addr, std::vector< uint8_t > data)
Definition rom.cc:658
bool is_loaded() const
Definition rom.h:144
void SetRoomIds(std::vector< uint16_t > room_ids)
static absl::Status LoadFromRom(Rom *rom, PitDamageTable *out)
const std::vector< uint16_t > & room_ids() const
absl::Status ReplaceRoomId(uint16_t old_room_id, uint16_t new_room_id)
bool Contains(uint16_t room_id) const
std::vector< uint16_t > room_ids_
absl::Status SaveToRom(Rom *rom) const
constexpr int kPitPointer
constexpr int kPitCount
constexpr int kNumberOfRooms
uint32_t SnesToPc(uint32_t addr) noexcept
Definition snes.h:8