yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
graphics_edit_context.cc
Go to the documentation of this file.
2
3#include <map>
4#include <utility>
5
6#include "absl/strings/str_format.h"
10#include "zelda3/dungeon/room.h"
11
12namespace yaze::editor {
13
15 constexpr const char* kLabels[] = {"Unflipped", "Horizontal", "Vertical",
16 "Horizontal + vertical"};
17 std::string label;
18 for (int orientation = 0; orientation < 4; ++orientation) {
19 if ((source_orientation_mask & (1 << orientation)) == 0) {
20 continue;
21 }
22 if (!label.empty()) {
23 label += ", ";
24 }
25 label += kLabels[orientation];
26 }
27 return label;
28}
29
31 const zelda3::GameData* owner) {
32 if (!target.writable_palette)
33 return absl::UnimplementedError(target.palette_address_limitation);
34 const auto& binding = *target.writable_palette;
35 if (!owner || binding.owner != owner)
36 return absl::FailedPreconditionError("Palette address session mismatch");
37 if (binding.colors[0] || target.palette.size() != 8 || binding.row < 2 ||
38 binding.row >= 8 ||
39 (binding.half_offset != 0 && binding.half_offset != 8)) {
40 return absl::FailedPreconditionError("Invalid raw palette address mapping");
41 }
42 for (int raw = 1; raw < 8; ++raw) {
43 if (!binding.colors[raw])
44 return absl::FailedPreconditionError("Nonzero palette index is unmapped");
45 auto color = zelda3::ReadPaletteColor(*owner, *binding.colors[raw]);
46 if (!color.ok())
47 return color.status();
48 const auto expected = color->rom_color();
49 const auto displayed = target.palette[raw].rom_color();
50 if (expected.red != displayed.red || expected.green != displayed.green ||
51 expected.blue != displayed.blue) {
52 return absl::FailedPreconditionError(
53 "Displayed palette differs from its writable source; refresh the "
54 "source");
55 }
56 }
57 return absl::OkStatus();
58}
59
61 GraphicsEditTarget& target, const zelda3::GameData* owner,
62 const zelda3::PaletteAddressMap& addresses, int row, int half_offset) {
63 target.writable_palette.reset();
65 row < 2 ? "HUD palette addressing is unavailable for this operation."
66 : "Animated, overridden or unresolved palette source has no "
67 "writable mapping.";
68 if (!owner || row < 0 || row >= 8 || (half_offset != 0 && half_offset != 8))
69 return;
71 binding.owner = owner;
72 binding.row = static_cast<uint8_t>(row);
73 binding.half_offset = static_cast<uint8_t>(half_offset);
74 for (int raw = 1; raw < 8; ++raw) {
75 binding.colors[raw] = addresses[row * 16 + half_offset + raw];
76 if (!binding.colors[raw])
77 return;
78 }
79 target.writable_palette = std::move(binding);
80 const auto status = ValidateGraphicsPaletteAddressing(target, owner);
81 if (!status.ok()) {
82 target.writable_palette.reset();
83 target.palette_address_limitation = std::string(status.message());
84 return;
85 }
86 target.palette_address_limitation.clear();
87}
88
90 GraphicsEditContext refreshed) {
91 if (context.owner != refreshed.owner ||
92 context.targets.size() != refreshed.targets.size()) {
93 return absl::FailedPreconditionError(
94 "Graphics source changed; reopen Edit Graphics from the source.");
95 }
96 for (size_t i = 0; i < context.targets.size(); ++i) {
97 const auto& old = context.targets[i];
98 const auto& next = refreshed.targets[i];
99 if (old.sheet_id != next.sheet_id || old.tile_index != next.tile_index ||
100 old.label != next.label ||
101 old.source_orientation_mask != next.source_orientation_mask ||
102 next.palette.size() != 8) {
103 return absl::FailedPreconditionError(
104 "Graphics source changed; reopen Edit Graphics from the source.");
105 }
106 if (next.writable_palette) {
107 const auto status =
109 if (!status.ok())
110 return status;
111 }
112 }
113 for (size_t i = 0; i < context.targets.size(); ++i) {
114 context.targets[i].writable_palette =
115 std::move(refreshed.targets[i].writable_palette);
116 context.targets[i].palette_address_limitation =
117 std::move(refreshed.targets[i].palette_address_limitation);
118 context.targets[i].palette = std::move(refreshed.targets[i].palette);
119 }
120 context.resolved = std::move(refreshed.resolved);
121 return absl::OkStatus();
122}
123
124absl::StatusOr<GraphicsEditContext> MakeDungeonGraphicsEditContext(
125 const zelda3::Room& room, const zelda3::RoomObject& object,
126 const zelda3::GameData* owner) {
127 if (!owner || room.game_data() != owner) {
128 return absl::FailedPreconditionError("Dungeon graphics session mismatch");
129 }
130 auto tiles = object.GetTiles();
131 if (!tiles.ok()) {
132 return tiles.status();
133 }
134 const auto patterns = zelda3::CustomObjectManager::Get().GetTilePatterns();
135 if (!patterns.ok())
136 return patterns.status();
137 std::vector<gfx::TileInfo> source_tiles;
138 if (*patterns && !zelda3::HasActiveCustomObjectOverride(object)) {
139 auto parsed = zelda3::ObjectParser(room.rom(), patterns->get())
140 .ParseObject(object.id_);
141 if (!parsed.ok())
142 return parsed.status();
143 source_tiles = std::move(*parsed);
144 tiles = std::span<const gfx::TileInfo>(source_tiles);
145 }
146 GraphicsEditContext context;
147 context.owner = owner;
148 context.label =
149 absl::StrFormat("Room %03X / Object %03X / Main %02X", room.id(),
150 object.id_, room.resolved_main_blockset());
154 const auto& colors = room.rendered_palette();
155 const auto blocks = room.blocks();
156 std::map<std::pair<uint16_t, uint8_t>, size_t> target_indices;
157 for (const auto& tile : *tiles) {
158 if (tile.id_ >= 512 || tile.palette_ >= 8 ||
159 (tile.id_ >= 0x1B0 && tile.id_ < 0x1D0)) {
160 context.limitation =
161 "Animated or unmapped tiles are omitted from pixel editing.";
162 continue;
163 }
164 const uint8_t orientation =
165 (tile.horizontal_mirror_ ? 1 : 0) | (tile.vertical_mirror_ ? 2 : 0);
166 const uint8_t orientation_bit = 1 << orientation;
167 const auto [entry, inserted] = target_indices.emplace(
168 std::make_pair(tile.id_, tile.palette_), context.targets.size());
169 if (!inserted) {
170 context.targets[entry->second].source_orientation_mask |= orientation_bit;
171 continue;
172 }
173 const int slot = tile.id_ / 64;
174 const int offset = tile.palette_ * 16;
175 if (colors.size() < static_cast<size_t>(offset + 16)) {
176 return absl::FailedPreconditionError("Room palette is not rendered");
177 }
178 const int half = room.BackgroundPaletteOffset(slot);
179 std::vector<gfx::SnesColor> palette;
180 for (int index = 0; index < 8; ++index) {
181 const auto& color = colors[offset + (index == 0 ? 0 : half + index)];
182 palette.emplace_back(color.r, color.g, color.b);
183 }
184 context.targets.push_back({
185 .sheet_id = blocks[slot],
186 .tile_index = static_cast<uint16_t>(tile.id_ % 64),
187 .label = absl::StrFormat("Tile %03X / slot %d / sheet %02X / row %d %s",
188 tile.id_, slot, blocks[slot], tile.palette_,
189 half == 8 ? "right" : "left"),
190 .palette = gfx::SnesPalette(palette),
191 .source_orientation_mask = orientation_bit,
192 });
193 ResolveGraphicsPaletteAddressing(context.targets.back(), owner,
195 tile.palette_, half);
196 }
197 if (context.targets.empty()) {
198 return absl::UnimplementedError(
199 "This object's tiles do not yet have an editable graphics source");
200 }
201 return context;
202}
203
204} // namespace yaze::editor
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
static CustomObjectManager & Get()
absl::StatusOr< std::shared_ptr< const DungeonTilePatterns > > GetTilePatterns()
Direct ROM parser for dungeon objects.
absl::StatusOr< std::vector< gfx::TileInfo > > ParseObject(int16_t object_id)
Parse object data directly from ROM.
const std::vector< SDL_Color > & rendered_palette() const
Definition room.h:1138
auto game_data()
Definition room.h:1086
auto rom() const
Definition room.h:1082
auto blocks() const
Definition room.h:1080
int BackgroundPaletteOffset(int slot) const
Definition room.cc:1085
const PaletteAddressMap & rendered_palette_addresses() const
Definition room.h:1135
uint8_t resolved_main_blockset() const
Definition room.h:1017
int id() const
Definition room.h:1012
Editors are the view controllers for the application.
absl::Status UpdateGraphicsContextPalettes(GraphicsEditContext &context, GraphicsEditContext refreshed)
void ResolveGraphicsPaletteAddressing(GraphicsEditTarget &target, const zelda3::GameData *owner, const zelda3::PaletteAddressMap &addresses, int row, int half_offset)
absl::StatusOr< GraphicsEditContext > MakeDungeonGraphicsEditContext(const zelda3::Room &room, const zelda3::RoomObject &object, const zelda3::GameData *owner)
absl::Status ValidateGraphicsPaletteAddressing(const GraphicsEditTarget &target, const zelda3::GameData *owner)
ResolvedGraphicsContext DescribeDungeonGraphicsContext(const Room &room, const GraphicsContextIdentity &identity)
ObjectLayerSemantics GetEffectiveObjectLayerSemantics(const RoomObject &object)
const char * ObjectRenderRoutingDisplayLabel(const ObjectLayerSemantics &semantics)
bool HasActiveCustomObjectOverride(const RoomObject &object)
absl::StatusOr< gfx::SnesColor > ReadPaletteColor(const GameData &owner, const PaletteColorAddress &address)
std::array< std::optional< PaletteColorAddress >, 128 > PaletteAddressMap
zelda3::ResolvedGraphicsContext resolved
std::vector< GraphicsEditTarget > targets
std::optional< GraphicsPaletteAddressing > writable_palette
std::array< std::optional< zelda3::PaletteColorAddress >, 8 > colors