7#include <unordered_map>
27 if (i < source.
size()) {
38 for (
int i = 0; i < palette.
size(); ++i) {
40 for (
size_t color = 0; color < padded.size(); ++color) {
48 if (palette.
empty()) {
51 if (requested >= 0 && requested <
static_cast<int>(palette.
size())) {
64 const std::vector<ObjectDrawer::TileTrace>& traces) {
70 layout.
object_id =
static_cast<int16_t
>(traces[0].object_id);
73 int min_x = traces[0].x_tile;
74 int min_y = traces[0].y_tile;
77 for (
const auto& t : traces) {
78 min_x = std::min(min_x,
static_cast<int>(t.x_tile));
79 min_y = std::min(min_y,
static_cast<int>(t.y_tile));
80 max_x = std::max(max_x,
static_cast<int>(t.x_tile));
81 max_y = std::max(max_y,
static_cast<int>(t.y_tile));
89 std::unordered_map<uint32_t, size_t> last_trace_by_cell;
90 last_trace_by_cell.reserve(traces.size());
91 for (
size_t i = 0; i < traces.size(); ++i) {
92 const auto& t = traces[i];
93 const uint32_t cell_key =
94 (
static_cast<uint32_t
>(
static_cast<uint16_t
>(t.x_tile)) << 16) |
95 static_cast<uint16_t
>(t.y_tile);
96 last_trace_by_cell[cell_key] = i;
99 std::vector<size_t> surviving_trace_indices;
100 surviving_trace_indices.reserve(last_trace_by_cell.size());
101 for (
const auto& [_, trace_index] : last_trace_by_cell) {
102 surviving_trace_indices.push_back(trace_index);
104 std::sort(surviving_trace_indices.begin(), surviving_trace_indices.end());
106 layout.
cells.reserve(surviving_trace_indices.size());
107 for (
size_t trace_index : surviving_trace_indices) {
108 const auto& t = traces[trace_index];
110 cell.
rel_x = t.x_tile - min_x;
111 cell.
rel_y = t.y_tile - min_y;
114 bool h_mirror = (t.flags & 0x1) != 0;
115 bool v_mirror = (t.flags & 0x2) != 0;
116 bool priority = (t.flags & 0x4) != 0;
117 uint8_t palette = (t.flags >> 3) & 0x7;
119 gfx::TileInfo(t.tile_id, palette, v_mirror, h_mirror, priority);
124 layout.
cells.push_back(cell);
132 const std::string& filename) {
143 layout.
cells.reserve(width * height);
144 for (
int y = 0; y < height; ++y) {
145 for (
int x = 0; x < width; ++x) {
153 layout.
cells.push_back(cell);
161 for (
auto& cell :
cells) {
162 if (cell.rel_x == rel_x && cell.rel_y == rel_y)
170 for (
const auto& cell :
cells) {
171 if (cell.rel_x == rel_x && cell.rel_y == rel_y)
178 for (
const auto& cell :
cells) {
186 for (
auto& cell :
cells) {
189 cell.modified =
false;
203 return absl::FailedPreconditionError(
"ROM not loaded");
213 constexpr uint8_t kPreviewSizeByte = 0x12;
214 auto [anchor_x, anchor_y] =
216 RoomObject obj(object_id, anchor_x, anchor_y, kPreviewSizeByte, 0);
221 bool is_custom =
false;
222 std::string custom_filename;
223 int subtype = obj.
size_ & 0x1F;
227 if (custom_result.ok()) {
237 std::vector<ObjectDrawer::TileTrace> traces;
244 auto status = drawer.
DrawObject(obj, dummy_bg1, dummy_bg2, palette);
251 if (traces.empty()) {
252 return absl::NotFoundError(
"Object produced no tile traces");
267 if (!room_gfx_buffer) {
268 return absl::FailedPreconditionError(
"No room graphics buffer");
270 if (layout.
cells.empty()) {
271 return absl::OkStatus();
278 std::vector<uint8_t> pixel_data(bmp_w * bmp_h, 0);
279 bitmap.
Create(bmp_w, bmp_h, 8, pixel_data);
283 if (!palette.
empty()) {
284 bitmap.
SetPalette(BuildCombinedPaletteBanks(palette));
290 for (
const auto& cell : layout.
cells) {
291 int px = cell.rel_x * 8;
292 int py = cell.rel_y * 8;
296 return absl::OkStatus();
300 const uint8_t* room_gfx_buffer,
302 int display_palette) {
303 if (!room_gfx_buffer) {
304 return absl::FailedPreconditionError(
"No room graphics buffer");
310 const int resolved_palette = ResolvePaletteIndex(palette, display_palette);
311 if (!palette.
empty()) {
313 BuildPaddedPaletteBank(palette.
palette_ref(resolved_palette)));
331 return absl::OkStatus();
336 return absl::OkStatus();
342 return absl::FailedPreconditionError(
343 "Custom object has no filename for write-back");
347 std::map<int, std::vector<const ObjectTileLayout::Cell*>> rows;
348 for (
const auto& cell : layout.
cells) {
349 rows[cell.rel_y].push_back(&cell);
353 std::vector<uint8_t> binary;
354 constexpr int kBufferStride = 128;
356 int prev_buffer_pos = 0;
357 for (
auto& [row_y, row_cells] : rows) {
360 row_cells.begin(), row_cells.end(),
361 [](
const auto* a,
const auto* b) { return a->rel_x < b->rel_x; });
363 int count =
static_cast<int>(row_cells.size());
364 int buffer_pos_for_row = row_y * kBufferStride + row_cells[0]->rel_x * 2;
365 int jump_offset = (row_y == rows.rbegin()->first)
370 uint16_t header = (count & 0x1F) | ((jump_offset & 0xFF) << 8);
371 binary.push_back(header & 0xFF);
372 binary.push_back((header >> 8) & 0xFF);
374 for (
const auto* cell : row_cells) {
376 binary.push_back(word & 0xFF);
377 binary.push_back((word >> 8) & 0xFF);
380 prev_buffer_pos = buffer_pos_for_row + count * 2;
389 std::filesystem::path full_path =
391 std::ofstream file(full_path, std::ios::binary);
393 return absl::InternalError(
"Failed to open file for writing: " +
396 file.write(
reinterpret_cast<const char*
>(binary.data()), binary.size());
401 return absl::OkStatus();
406 return absl::FailedPreconditionError(
407 "No tile data address for ROM write-back");
410 for (
size_t i = 0; i < layout.
cells.size(); ++i) {
411 const auto& cell = layout.
cells[i];
415 const int write_index =
416 cell.write_index >= 0 ? cell.write_index :
static_cast<int>(i);
421 auto low =
static_cast<uint8_t
>(word & 0xFF);
422 auto high =
static_cast<uint8_t
>((word >> 8) & 0xFF);
427 return absl::OkStatus();
444 for (
int id = 0;
id < 0x100; ++id) {
445 RoomObject scan_obj(
static_cast<int16_t
>(
id), 0, 0, 0, 0);
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::Status WriteByte(int addr, uint8_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()
absl::Status WriteBack(const ObjectTileLayout &layout)
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)
int CountObjectsSharingTileData(int16_t object_id) const
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)
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)
gfx::SnesPalette BuildPaddedPaletteBank(const gfx::SnesPalette &source)
constexpr int kPaletteBankSize
int ResolvePaletteIndex(const gfx::PaletteGroup &palette, int requested)
#define RETURN_IF_ERROR(expr)
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
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)