9#include <unordered_map>
10#include <unordered_set>
12#include "absl/strings/str_format.h"
32 if (i < source.
size()) {
43 for (
int i = 0; i < palette.
size(); ++i) {
45 for (
size_t color = 0; color < padded.size(); ++color) {
53 if (palette.
empty()) {
56 if (requested >= 0 && requested <
static_cast<int>(palette.
size())) {
70bool RangesOverlap(int64_t lhs_start, int64_t lhs_end, int64_t rhs_start,
72 return lhs_start < rhs_end && rhs_start < lhs_end;
76 return RangesOverlap(lhs_address, lhs_address + 2, rhs_address,
83 return absl::FailedPreconditionError(
84 "Editable object cell is outside the audited 2x2 layout");
89 return static_cast<size_t>(cell.
rel_x * 2 + cell.
rel_y);
99 const std::vector<ObjectDrawer::TileTrace>& traces) {
101 if (traces.empty()) {
105 layout.
object_id =
static_cast<int16_t
>(traces[0].object_id);
108 int min_x = traces[0].x_tile;
109 int min_y = traces[0].y_tile;
112 for (
const auto& t : traces) {
113 min_x = std::min(min_x,
static_cast<int>(t.x_tile));
114 min_y = std::min(min_y,
static_cast<int>(t.y_tile));
115 max_x = std::max(max_x,
static_cast<int>(t.x_tile));
116 max_y = std::max(max_y,
static_cast<int>(t.y_tile));
124 std::unordered_map<uint32_t, size_t> last_trace_by_cell;
125 last_trace_by_cell.reserve(traces.size());
126 for (
size_t i = 0; i < traces.size(); ++i) {
127 const auto& t = traces[i];
128 const uint32_t cell_key =
129 (
static_cast<uint32_t
>(
static_cast<uint16_t
>(t.x_tile)) << 16) |
130 static_cast<uint16_t
>(t.y_tile);
131 last_trace_by_cell[cell_key] = i;
134 std::vector<size_t> surviving_trace_indices;
135 surviving_trace_indices.reserve(last_trace_by_cell.size());
136 for (
const auto& [_, trace_index] : last_trace_by_cell) {
137 surviving_trace_indices.push_back(trace_index);
139 std::sort(surviving_trace_indices.begin(), surviving_trace_indices.end());
141 layout.
cells.reserve(surviving_trace_indices.size());
142 for (
size_t trace_index : surviving_trace_indices) {
143 const auto& t = traces[trace_index];
145 cell.
rel_x = t.x_tile - min_x;
146 cell.
rel_y = t.y_tile - min_y;
149 bool h_mirror = (t.flags & 0x1) != 0;
150 bool v_mirror = (t.flags & 0x2) != 0;
151 bool priority = (t.flags & 0x4) != 0;
152 uint8_t palette = (t.flags >> 3) & 0x7;
154 gfx::TileInfo(t.tile_id, palette, v_mirror, h_mirror, priority);
159 layout.
cells.push_back(cell);
167 const std::string& filename) {
178 layout.
cells.reserve(width * height);
179 for (
int y = 0; y < height; ++y) {
180 for (
int x = 0; x < width; ++x) {
188 layout.
cells.push_back(cell);
196 for (
auto& cell :
cells) {
197 if (cell.rel_x == rel_x && cell.rel_y == rel_y)
205 for (
const auto& cell :
cells) {
206 if (cell.rel_x == rel_x && cell.rel_y == rel_y)
213 for (
const auto& cell :
cells) {
221 for (
auto& cell :
cells) {
224 cell.modified =
false;
243 uint8_t object_size) {
245 return absl::FailedPreconditionError(
"ROM not loaded");
256 auto [anchor_x, anchor_y] =
258 RoomObject obj(object_id, anchor_x, anchor_y, preview_size, 0);
263 bool is_custom =
false;
264 std::string custom_filename;
265 int subtype = obj.
size_ & 0x1F;
269 if (custom_result.ok()) {
279 std::vector<ObjectDrawer::TileTrace> traces;
286 auto status = drawer.
DrawObject(obj, dummy_bg1, dummy_bg2, palette);
293 if (traces.empty()) {
294 return absl::NotFoundError(
"Object produced no tile traces");
311 return std::find(kEditableStandardObjectIds.begin(),
312 kEditableStandardObjectIds.end(),
313 object_id) != kEditableStandardObjectIds.end();
319 return absl::UnimplementedError(
320 "Standard object tile editing is not supported for this object");
323 return absl::FailedPreconditionError(
"ROM not loaded");
326 const uint32_t descriptor_pc_address =
328 auto descriptor_or =
rom_->
ReadWord(
static_cast<int>(descriptor_pc_address));
329 if (!descriptor_or.ok()) {
330 return descriptor_or.status();
332 const uint16_t descriptor_word = *descriptor_or;
333 const int64_t source_address =
335 if (source_address < 0 ||
336 source_address +
static_cast<int64_t
>(kFixed2x2SourceWordCount * 2) >
337 static_cast<int64_t
>(
rom_->
size()) ||
338 source_address > std::numeric_limits<int>::max()) {
339 return absl::OutOfRangeError(
340 "Editable object tile source is outside the loaded ROM");
342 const int64_t source_end =
343 source_address +
static_cast<int64_t
>(kFixed2x2SourceWordCount * 2);
344 if (RangesOverlap(source_address, source_end, kObjectMetadataStart,
345 kObjectMetadataEnd)) {
346 return absl::FailedPreconditionError(
347 "Editable object tile source overlaps object descriptor or routine "
352 if (!layout_or.ok()) {
353 return layout_or.status();
357 return absl::FailedPreconditionError(
358 "Custom objects do not use standard ROM source provenance");
361 layout.
cells.size() != kFixed2x2SourceWordCount ||
363 return absl::FailedPreconditionError(
364 "Object draw does not match the audited fixed 2x2 layout");
368 span.
pc_address =
static_cast<uint32_t
>(source_address);
370 for (
size_t word_index = 0; word_index < kFixed2x2SourceWordCount;
375 return word_or.status();
380 std::array<bool, kFixed2x2SourceWordCount> mapped_words{};
381 for (
auto& cell : layout.
cells) {
382 auto source_word_index_or = ResolveFixed2x2SourceWordIndex(cell);
383 if (!source_word_index_or.ok()) {
384 return source_word_index_or.status();
386 const size_t source_word_index = *source_word_index_or;
387 if (mapped_words[source_word_index]) {
388 return absl::FailedPreconditionError(
389 "Editable object layout maps multiple cells to one source word");
391 if (cell.original_word != span.
expected_words[source_word_index]) {
392 return absl::FailedPreconditionError(
393 "Draw trace does not match the resolved ROM source word");
395 mapped_words[source_word_index] =
true;
398 if (std::find(mapped_words.begin(), mapped_words.end(),
false) !=
399 mapped_words.end()) {
400 return absl::FailedPreconditionError(
401 "Editable object layout does not cover every source word");
408 provenance.
spans.push_back(std::move(span));
417 if (!room_gfx_buffer) {
418 return absl::FailedPreconditionError(
"No room graphics buffer");
420 if (layout.
cells.empty()) {
421 return absl::OkStatus();
428 std::vector<uint8_t> pixel_data(bmp_w * bmp_h, 0);
429 bitmap.
Create(bmp_w, bmp_h, 8, pixel_data);
433 if (!palette.
empty()) {
434 bitmap.
SetPalette(BuildCombinedPaletteBanks(palette));
440 for (
const auto& cell : layout.
cells) {
441 int px = cell.rel_x * 8;
442 int py = cell.rel_y * 8;
446 return absl::OkStatus();
450 const uint8_t* room_gfx_buffer,
452 int display_palette) {
453 if (!room_gfx_buffer) {
454 return absl::FailedPreconditionError(
"No room graphics buffer");
460 const int resolved_palette = ResolvePaletteIndex(palette, display_palette);
461 if (!palette.
empty()) {
463 BuildPaddedPaletteBank(palette.
palette_ref(resolved_palette)));
481 return absl::OkStatus();
486 return absl::OkStatus();
492 return absl::FailedPreconditionError(
493 "Custom object has no filename for write-back");
497 std::map<int, std::vector<const ObjectTileLayout::Cell*>> rows;
498 for (
const auto& cell : layout.
cells) {
499 rows[cell.rel_y].push_back(&cell);
503 std::vector<uint8_t> binary;
504 constexpr int kBufferStride = 128;
506 int prev_buffer_pos = 0;
507 for (
auto& [row_y, row_cells] : rows) {
510 row_cells.begin(), row_cells.end(),
511 [](
const auto* a,
const auto* b) { return a->rel_x < b->rel_x; });
513 int count =
static_cast<int>(row_cells.size());
514 int buffer_pos_for_row = row_y * kBufferStride + row_cells[0]->rel_x * 2;
515 int jump_offset = (row_y == rows.rbegin()->first)
520 uint16_t header = (count & 0x1F) | ((jump_offset & 0xFF) << 8);
521 binary.push_back(header & 0xFF);
522 binary.push_back((header >> 8) & 0xFF);
524 for (
const auto* cell : row_cells) {
526 binary.push_back(word & 0xFF);
527 binary.push_back((word >> 8) & 0xFF);
530 prev_buffer_pos = buffer_pos_for_row + count * 2;
539 std::filesystem::path full_path =
541 std::ofstream file(full_path, std::ios::binary);
543 return absl::InternalError(
"Failed to open file for writing: " +
546 file.write(
reinterpret_cast<const char*
>(binary.data()), binary.size());
551 return absl::OkStatus();
556 return plan_or.status();
564 return absl::InvalidArgumentError(
565 "Cannot build a standard ROM write plan for a custom object");
570 return absl::FailedPreconditionError(
"ROM not loaded");
573 return absl::FailedPreconditionError(
574 "Standard object layout has no editable source provenance");
577 return absl::UnimplementedError(
578 "Standard object tile editing is not supported for this object");
582 if (provenance.object_id != layout.
object_id) {
583 return absl::FailedPreconditionError(
584 "Object tile source provenance does not match the layout object");
586 const uint32_t expected_descriptor_address =
static_cast<uint32_t
>(
588 if (provenance.descriptor_pc_address != expected_descriptor_address) {
589 return absl::FailedPreconditionError(
590 "Object tile source descriptor address does not match the object");
592 if (provenance.spans.size() != 1 ||
593 provenance.spans.front().expected_words.size() !=
594 kFixed2x2SourceWordCount) {
595 return absl::FailedPreconditionError(
596 "Object tile source provenance does not match the audited source map");
599 const int64_t rom_size =
static_cast<int64_t
>(
rom_->
size());
600 if (provenance.descriptor_pc_address >
601 static_cast<uint32_t
>(std::numeric_limits<int>::max()) ||
602 static_cast<int64_t
>(provenance.descriptor_pc_address) + 2 > rom_size) {
603 return absl::OutOfRangeError(
604 "Object tile source descriptor is outside the loaded ROM");
606 auto current_descriptor_or =
607 rom_->
ReadWord(
static_cast<int>(provenance.descriptor_pc_address));
608 if (!current_descriptor_or.ok()) {
609 return current_descriptor_or.status();
611 if (*current_descriptor_or != provenance.expected_descriptor_word) {
612 return absl::FailedPreconditionError(
613 "Object tile source descriptor changed after capture");
616 const auto& span = provenance.spans.front();
617 const int64_t expected_span_address =
619 provenance.expected_descriptor_word;
620 if (span.pc_address != expected_span_address) {
621 return absl::FailedPreconditionError(
622 "Object tile source span does not match its descriptor");
624 if (span.pc_address >
625 static_cast<uint32_t
>(std::numeric_limits<int>::max()) ||
626 static_cast<int64_t
>(span.pc_address) +
627 static_cast<int64_t
>(span.expected_words.size() * 2) >
629 return absl::OutOfRangeError(
630 "Object tile source span is outside the loaded ROM");
633 const int64_t span_start =
static_cast<int64_t
>(span.pc_address);
634 const int64_t span_end =
635 span_start +
static_cast<int64_t
>(span.expected_words.size() * 2);
636 if (RangesOverlap(span_start, span_end, kObjectMetadataStart,
637 kObjectMetadataEnd)) {
638 return absl::FailedPreconditionError(
639 "Object tile source span overlaps object descriptor or routine "
643 std::vector<int> precondition_addresses;
644 const int descriptor_address =
645 static_cast<int>(provenance.descriptor_pc_address);
646 precondition_addresses.push_back(descriptor_address);
648 {descriptor_address, provenance.expected_descriptor_word});
649 for (
size_t word_index = 0; word_index < span.expected_words.size();
651 const int address =
static_cast<int>(span.pc_address + word_index * 2);
652 if (std::any_of(precondition_addresses.begin(),
653 precondition_addresses.end(), [&](
int existing_address) {
654 return WordRangesOverlap(existing_address, address);
656 return absl::FailedPreconditionError(
657 "Object tile descriptor and source preconditions overlap");
659 precondition_addresses.push_back(address);
661 if (!current_word_or.ok()) {
662 return current_word_or.status();
664 if (*current_word_or != span.expected_words[word_index]) {
665 return absl::FailedPreconditionError(
666 "Object tile source word changed after capture");
668 plan.
preconditions_.push_back({address, span.expected_words[word_index]});
671 if (layout.
cells.size() != kFixed2x2SourceWordCount ||
673 return absl::FailedPreconditionError(
674 "Object tile layout does not match the audited fixed 2x2 shape");
677 std::unordered_set<int> resolved_addresses;
678 for (
const auto& cell : layout.
cells) {
679 if (!cell.source_ref.has_value()) {
680 return absl::FailedPreconditionError(
681 "Object tile cell has no editable source reference");
683 const auto& source_ref = *cell.source_ref;
684 if (source_ref.span_index >= provenance.spans.size()) {
685 return absl::FailedPreconditionError(
686 "Object tile source span reference is out of bounds");
688 const auto& referenced_span = provenance.spans[source_ref.span_index];
689 if (source_ref.word_index >= referenced_span.expected_words.size()) {
690 return absl::FailedPreconditionError(
691 "Object tile source word reference is out of bounds");
694 auto expected_source_index_or = ResolveFixed2x2SourceWordIndex(cell);
695 if (!expected_source_index_or.ok()) {
696 return expected_source_index_or.status();
698 if (source_ref.span_index != 0 ||
699 source_ref.word_index != *expected_source_index_or) {
700 return absl::FailedPreconditionError(
701 "Object tile cell source reference does not match the audited map");
704 const int64_t address64 =
static_cast<int64_t
>(referenced_span.pc_address) +
705 static_cast<int64_t
>(source_ref.word_index) * 2;
706 if (address64 < 0 || address64 + 2 > rom_size ||
707 address64 > std::numeric_limits<int>::max()) {
708 return absl::OutOfRangeError(
709 "Object tile write range is outside the loaded ROM");
711 const int address =
static_cast<int>(address64);
712 if (!resolved_addresses.insert(address).second) {
713 return absl::FailedPreconditionError(
714 "Object tile cells resolve to a duplicate ROM address");
717 const uint16_t expected_word =
718 referenced_span.expected_words[source_ref.word_index];
719 if (cell.original_word != expected_word) {
720 return absl::FailedPreconditionError(
721 "Object tile cell original word does not match its source");
724 if (!cell.modified && current_layout_word != cell.original_word) {
725 return absl::FailedPreconditionError(
726 "Object tile cell changed without being marked modified");
728 if (!cell.modified) {
732 plan.
writes_.push_back({address, expected_word, current_layout_word});
733 plan.
write_ranges_.emplace_back(
static_cast<uint32_t
>(address),
734 static_cast<uint32_t
>(address + 2));
743 return absl::OkStatus();
746 return absl::FailedPreconditionError(
747 "Object tile write plan ranges do not match its writes");
750 return absl::FailedPreconditionError(
751 "Object tile write plan has no source provenance preconditions");
754 return absl::FailedPreconditionError(
"ROM not loaded");
757 const int64_t rom_size =
static_cast<int64_t
>(
rom_->
size());
758 std::unordered_map<int, uint16_t> precondition_words;
759 std::vector<int> precondition_addresses;
761 const int64_t address =
static_cast<int64_t
>(precondition.address);
762 if (address < 0 || address + 2 > rom_size) {
763 return absl::OutOfRangeError(
764 "Object tile precondition is outside the loaded ROM");
766 if (std::any_of(precondition_addresses.begin(),
767 precondition_addresses.end(), [&](
int existing_address) {
768 return WordRangesOverlap(existing_address,
769 precondition.address);
771 return absl::FailedPreconditionError(
772 "Object tile plan has overlapping CAS preconditions");
774 precondition_addresses.push_back(precondition.address);
775 if (!precondition_words
776 .emplace(precondition.address, precondition.expected_word)
778 return absl::FailedPreconditionError(
779 "Object tile plan has duplicate CAS preconditions");
783 std::unordered_set<int> write_addresses;
784 std::vector<int> validated_write_addresses;
785 for (
size_t write_index = 0; write_index < plan.
writes_.size();
787 const auto& write = plan.
writes_[write_index];
788 const int64_t address =
static_cast<int64_t
>(write.address);
789 if (address < 0 || address + 2 > rom_size) {
790 return absl::OutOfRangeError(
791 "Object tile write range is outside the loaded ROM");
793 if (std::any_of(validated_write_addresses.begin(),
794 validated_write_addresses.end(), [&](
int existing_address) {
795 return WordRangesOverlap(existing_address, write.address);
797 return absl::FailedPreconditionError(
798 "Object tile plan has overlapping write ranges");
800 validated_write_addresses.push_back(write.address);
801 if (!write_addresses.insert(write.address).second) {
802 return absl::FailedPreconditionError(
803 "Object tile plan has duplicate write addresses");
805 const std::pair<uint32_t, uint32_t> expected_range = {
806 static_cast<uint32_t
>(write.address),
807 static_cast<uint32_t
>(write.address + 2)};
809 return absl::FailedPreconditionError(
810 "Object tile write plan range does not match its write address");
812 const auto precondition = precondition_words.find(write.address);
813 if (precondition == precondition_words.end() ||
814 precondition->second != write.expected_word) {
815 return absl::FailedPreconditionError(
816 "Object tile write has no matching source precondition");
823 auto current_word_or =
rom_->
ReadWord(precondition.address);
824 if (!current_word_or.ok()) {
825 return current_word_or.status();
827 if (*current_word_or != precondition.expected_word) {
828 return absl::FailedPreconditionError(
829 "Object tile source changed after the write plan was built");
832 for (
const auto& write : plan.
writes_) {
834 if (!current_word_or.ok()) {
835 return current_word_or.status();
837 if (*current_word_or != write.expected_word) {
838 return absl::FailedPreconditionError(
839 "Object tile write target changed after the plan was built");
845 for (
const auto& write : plan.
writes_) {
846 transaction.
WriteWord(write.address, write.word);
848 const absl::Status status = transaction.
Commit();
854 return absl::OkStatus();
857absl::StatusOr<ObjectTileSourceImpact>
861 return absl::FailedPreconditionError(
"ROM not loaded");
864 return absl::InvalidArgumentError(
865 "Custom objects do not use standard ROM tile sources");
868 return absl::FailedPreconditionError(
869 "Standard object layout has no editable source provenance");
877 return plan_or.status();
880 std::vector<ObjectTileReadRange> target_ranges;
882 if (span.expected_words.empty()) {
883 return absl::FailedPreconditionError(
884 "Editable object tile source span is empty");
886 const uint64_t end =
static_cast<uint64_t
>(span.pc_address) +
887 static_cast<uint64_t
>(span.expected_words.size()) * 2;
888 if (end >
rom_->
size() || end > std::numeric_limits<uint32_t>::max()) {
889 return absl::OutOfRangeError(
890 "Editable object tile source span is outside the loaded ROM");
892 target_ranges.push_back({span.pc_address,
static_cast<uint32_t
>(end)});
895 const auto overlaps_target =
897 return std::any_of(target_ranges.begin(), target_ranges.end(),
899 return candidate.begin < target.end &&
900 target.begin < candidate.end;
906 const auto inspect_object = [&](
int object_id) -> absl::Status {
909 if (!ranges_or.ok()) {
910 return absl::FailedPreconditionError(absl::StrFormat(
911 "Unable to resolve tile sources for object 0x%03X: %s", object_id,
912 ranges_or.status().message()));
916 entry.
object_id =
static_cast<int16_t
>(object_id);
917 for (
const auto& range : *ranges_or) {
918 if (overlaps_target(range)) {
925 return absl::OkStatus();
931 for (
int object_id = 0x000; object_id <= 0x0F7; ++object_id) {
932 const absl::Status status = inspect_object(object_id);
937 for (
int object_id = 0x100; object_id <= 0x13F; ++object_id) {
938 const absl::Status status = inspect_object(object_id);
943 for (
int object_id = 0xF80; object_id <= 0xFFF; ++object_id) {
944 const absl::Status status = inspect_object(object_id);
954 constexpr int kLightableTorchTileCount = 4;
961 for (
const auto consumer :
965 for (
const auto& range : kLightableTorchSources) {
966 if (overlaps_target(range)) {
970 if (!torch_impact.overlapping_ranges.empty()) {
975 const bool includes_edited_object = std::any_of(
977 [&](
const auto& entry) { return entry.object_id == layout.object_id; });
978 if (!includes_edited_object) {
979 return absl::FailedPreconditionError(
980 "Edited object is missing from its tile source impact inventory");
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
void set_dirty(bool dirty)
absl::StatusOr< uint16_t > ReadWord(int offset) const
void AdvanceObjectTileRevision()
Transaction & WriteWord(int address, uint16_t value)
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 SetPalette(const SnesPalette &palette)
Set the palette for the bitmap using SNES palette format.
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
void AddColor(const SnesColor &color)
SNES 16-bit tile metadata container.
static CustomObjectManager & Get()
absl::StatusOr< std::shared_ptr< CustomObject > > GetObjectInternal(int object_id, int subtype)
std::string ResolveFilename(int object_id, int subtype) const
Draws dungeon objects to background buffers using game patterns.
void DrawTileToBitmap(gfx::Bitmap &bitmap, const gfx::TileInfo &tile_info, int pixel_x, int pixel_y, const uint8_t *tiledata)
Draw a single tile directly to bitmap.
void ClearTraceCollector()
absl::Status DrawObject(const RoomObject &object, gfx::BackgroundBuffer &bg1, gfx::BackgroundBuffer &bg2, const gfx::PaletteGroup &palette_group, const DungeonState *state=nullptr, gfx::BackgroundBuffer *layout_bg1=nullptr)
Draw a room object to background buffers.
void SetTraceCollector(std::vector< TileTrace > *collector, bool trace_only=false)
std::pair< int, int > ResolveAnchor(int16_t object_id, uint8_t size_byte) const
Resolve the canvas anchor (x, y) for a given object's draw routine.
static ObjectGeometry & Get()
Direct ROM parser for dungeon objects.
absl::StatusOr< std::vector< ObjectTileReadRange > > ResolveTileReadRanges(int16_t object_id)
Resolve every tile-data range consumed by ParseObject.
absl::Status WriteBack(const ObjectTileLayout &layout)
absl::StatusOr< ObjectTileSourceImpact > AnalyzeStandardTileSourceImpact(const ObjectTileLayout &layout) const
absl::Status ApplyStandardWritePlan(const ObjectTileWritePlan &plan)
static constexpr int kAtlasTilesPerRow
ObjectTileEditor(Rom *rom)
absl::Status RenderLayoutToBitmap(const ObjectTileLayout &layout, gfx::Bitmap &bitmap, const uint8_t *room_gfx_buffer, const gfx::PaletteGroup &palette)
static bool IsEditableStandardObject(int16_t object_id)
absl::StatusOr< ObjectTileLayout > CaptureObjectLayout(int16_t object_id, const Room &room, const gfx::PaletteGroup &palette)
static constexpr int kAtlasWidthPx
static constexpr int kAtlasTileCount
static constexpr int kAtlasHeightPx
absl::Status BuildTile8Atlas(gfx::Bitmap &atlas, const uint8_t *room_gfx_buffer, const gfx::PaletteGroup &palette, int display_palette=2)
absl::StatusOr< ObjectTileLayout > CaptureEditableObjectLayout(int16_t object_id, const Room &room, const gfx::PaletteGroup &palette)
absl::StatusOr< ObjectTileWritePlan > BuildStandardWritePlan(const ObjectTileLayout &layout) const
Fully resolved standard-object ROM writes.
std::vector< std::pair< uint32_t, uint32_t > > write_ranges_
std::vector< Write > writes_
std::vector< WordPrecondition > preconditions_
const std::array< uint8_t, 0x10000 > & get_gfx_buffer() const
uint16_t TileInfoToWord(TileInfo tile_info)
TileInfo WordToTileInfo(uint16_t word)
gfx::SnesPalette BuildCombinedPaletteBanks(const gfx::PaletteGroup &palette)
constexpr int64_t kObjectMetadataEnd
constexpr int64_t kObjectMetadataStart
bool WordRangesOverlap(int64_t lhs_address, int64_t rhs_address)
absl::StatusOr< size_t > ResolveFixed2x2SourceWordIndex(const ObjectTileLayout::Cell &cell)
gfx::SnesPalette BuildPaddedPaletteBank(const gfx::SnesPalette &source)
constexpr int kPaletteBankSize
int ResolvePaletteIndex(const gfx::PaletteGroup &palette, int requested)
bool RangesOverlap(int64_t lhs_start, int64_t lhs_end, int64_t rhs_start, int64_t rhs_end)
constexpr size_t kFixed2x2SourceWordCount
constexpr std::array< int16_t, 2 > kEditableStandardObjectIds
uint8_t DefaultRoomObjectSizeForPlacement(int object_id)
constexpr int kRoomObjectSubtype3
uint8_t CanonicalRoomObjectSize(int object_id, uint8_t requested_size)
constexpr int kRoomObjectSubtype1
constexpr int kRoomObjectSubtype2
constexpr int kRoomObjectTileAddress
Represents a group of palettes.
const SnesPalette & palette_ref(int i) const
Editable tile8 layout captured from an object's draw trace.
bool HasModifications() const
std::optional< ObjectTileSourceProvenance > source_provenance
static ObjectTileLayout CreateEmpty(int width, int height, int16_t object_id, const std::string &filename)
Cell * FindCell(int rel_x, int rel_y)
std::vector< Cell > cells
std::string custom_filename
static ObjectTileLayout FromTraces(const std::vector< ObjectDrawer::TileTrace > &traces)
One half-open PC range consumed while parsing an object's tile data.
std::vector< ObjectTileReadRange > overlapping_ranges
One standard object whose parser-visible tile sources overlap an editable layout's authorized source ...
std::vector< ObjectTileReadRange > overlapping_ranges
Fail-closed impact inventory within ObjectParser's source model plus explicitly modeled runtime consu...
std::vector< ObjectTileRuntimeConsumerImpactEntry > runtime_consumers
std::vector< ObjectTileSourceImpactEntry > affected_objects
ROM descriptor and source snapshot authorizing an editable capture.
uint16_t expected_descriptor_word
uint32_t descriptor_pc_address
std::vector< ObjectTileSourceSpan > spans
A cell's exact word within an authorized source span.
One contiguous ROM-backed source span for an object's tile words.
std::vector< uint16_t > expected_words