23 const std::vector<ObjectDrawer::TileTrace>& traces) {
25 if (traces.empty())
return layout;
27 layout.
object_id =
static_cast<int16_t
>(traces[0].object_id);
30 int min_x = traces[0].x_tile;
31 int min_y = traces[0].y_tile;
34 for (
const auto& t : traces) {
35 min_x = std::min(min_x,
static_cast<int>(t.x_tile));
36 min_y = std::min(min_y,
static_cast<int>(t.y_tile));
37 max_x = std::max(max_x,
static_cast<int>(t.x_tile));
38 max_y = std::max(max_y,
static_cast<int>(t.y_tile));
46 layout.
cells.reserve(traces.size());
47 for (
const auto& t : traces) {
49 cell.
rel_x = t.x_tile - min_x;
50 cell.
rel_y = t.y_tile - min_y;
53 bool h_mirror = (t.flags & 0x1) != 0;
54 bool v_mirror = (t.flags & 0x2) != 0;
55 bool priority = (t.flags & 0x4) != 0;
56 uint8_t palette = (t.flags >> 3) & 0x7;
61 layout.
cells.push_back(cell);
69 const std::string& filename) {
80 layout.
cells.reserve(width * height);
81 for (
int y = 0; y < height; ++y) {
82 for (
int x = 0; x < width; ++x) {
89 layout.
cells.push_back(cell);
97 for (
auto& cell :
cells) {
98 if (cell.rel_x == rel_x && cell.rel_y == rel_y)
return &cell;
105 for (
const auto& cell :
cells) {
106 if (cell.rel_x == rel_x && cell.rel_y == rel_y)
return &cell;
112 for (
const auto& cell :
cells) {
113 if (cell.modified)
return true;
119 for (
auto& cell :
cells) {
122 cell.modified =
false;
134 int16_t object_id,
const Room& room,
137 return absl::FailedPreconditionError(
"ROM not loaded");
146 bool is_custom =
false;
147 std::string custom_filename;
148 int subtype = obj.
size_ & 0x1F;
152 if (custom_result.ok()) {
162 std::vector<ObjectDrawer::TileTrace> traces;
169 auto status = drawer.
DrawObject(obj, dummy_bg1, dummy_bg2, palette);
176 if (traces.empty()) {
177 return absl::NotFoundError(
"Object produced no tile traces");
191 const uint8_t* room_gfx_buffer,
193 if (!room_gfx_buffer) {
194 return absl::FailedPreconditionError(
"No room graphics buffer");
196 if (layout.
cells.empty()) {
197 return absl::OkStatus();
204 std::vector<uint8_t> pixel_data(bmp_w * bmp_h, 0);
205 bitmap.
Create(bmp_w, bmp_h, 8, pixel_data);
208 if (palette.
size() > 2) {
210 }
else if (palette.
size() > 0) {
217 for (
const auto& cell : layout.
cells) {
218 int px = cell.rel_x * 8;
219 int py = cell.rel_y * 8;
223 return absl::OkStatus();
227 gfx::Bitmap& atlas,
const uint8_t* room_gfx_buffer,
229 if (!room_gfx_buffer) {
230 return absl::FailedPreconditionError(
"No room graphics buffer");
233 std::vector<uint8_t> pixel_data(
237 if (palette.
size() >
static_cast<size_t>(display_palette)) {
239 }
else if (palette.
size() > 0) {
252 static_cast<uint8_t
>(display_palette),
253 false,
false,
false);
257 return absl::OkStatus();
262 return absl::OkStatus();
268 return absl::FailedPreconditionError(
269 "Custom object has no filename for write-back");
273 std::map<int, std::vector<const ObjectTileLayout::Cell*>> rows;
274 for (
const auto& cell : layout.
cells) {
275 rows[cell.rel_y].push_back(&cell);
279 std::vector<uint8_t> binary;
280 constexpr int kBufferStride = 128;
282 int prev_buffer_pos = 0;
283 for (
auto& [row_y, row_cells] : rows) {
285 std::sort(row_cells.begin(), row_cells.end(),
286 [](
const auto* a,
const auto* b) {
287 return a->rel_x < b->rel_x;
290 int count =
static_cast<int>(row_cells.size());
291 int buffer_pos_for_row = row_y * kBufferStride + row_cells[0]->rel_x * 2;
293 (row_y == rows.rbegin()->first)
298 uint16_t header = (count & 0x1F) | ((jump_offset & 0xFF) << 8);
299 binary.push_back(header & 0xFF);
300 binary.push_back((header >> 8) & 0xFF);
302 for (
const auto* cell : row_cells) {
304 binary.push_back(word & 0xFF);
305 binary.push_back((word >> 8) & 0xFF);
308 prev_buffer_pos = buffer_pos_for_row + count * 2;
317 std::filesystem::path full_path =
319 std::ofstream file(full_path, std::ios::binary);
321 return absl::InternalError(
"Failed to open file for writing: " +
324 file.write(
reinterpret_cast<const char*
>(binary.data()), binary.size());
329 return absl::OkStatus();
334 return absl::FailedPreconditionError(
335 "No tile data address for ROM write-back");
338 for (
size_t i = 0; i < layout.
cells.size(); ++i) {
339 const auto& cell = layout.
cells[i];
340 if (!cell.modified)
continue;
346 auto low =
static_cast<uint8_t
>(word & 0xFF);
347 auto high =
static_cast<uint8_t
>((word >> 8) & 0xFF);
352 return absl::OkStatus();
363 if (target_ptr < 0)
return 0;
367 for (
int id = 0;
id < 0x100; ++id) {
368 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.
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)
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)
#define RETURN_IF_ERROR(expr)
Represents a group of palettes.
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)