From eccebd48ac6c311023052386155d685dadfef202 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Mon, 22 Feb 2021 17:39:57 -0500 Subject: Modified map/tileset dumper to coalesce tilesets Multiple map IDs can be provided now and their tilesets will be combined. --- tools/sprite_dumper/identifier.h | 26 ++- tools/sprite_dumper/tileset_dumper.cpp | 402 +++++++++++++++++++-------------- 2 files changed, 250 insertions(+), 178 deletions(-) diff --git a/tools/sprite_dumper/identifier.h b/tools/sprite_dumper/identifier.h index 74d83ce..4aac943 100644 --- a/tools/sprite_dumper/identifier.h +++ b/tools/sprite_dumper/identifier.h @@ -5,6 +5,15 @@ #include template +class DefaultKeyExtract { +public: + + const T& operator()(const T& val) const { + return val; + } +}; + +template > class identifier { public: @@ -12,20 +21,21 @@ public: private: + using key_type = std::remove_reference_t>; using vector_type = std::vector; public: - using key_type = typename vector_type::size_type; + using id_type = typename vector_type::size_type; - key_type add(const value_type& val) + id_type add(const value_type& val) { - auto it = ids_.find(val); + auto it = ids_.find(KeyExtract()(val)); if (it == std::end(ids_)) { - key_type ret = ids_.size(); - ids_[val] = ret; + id_type ret = ids_.size(); + ids_[KeyExtract()(val)] = ret; uniq_.push_back(val); @@ -40,19 +50,19 @@ public: ids_.clear(); } - inline const value_type& get(key_type i) const + inline const value_type& get(id_type i) const { return uniq_.at(i); } - inline key_type size() const + inline id_type size() const { return uniq_.size(); } private: - std::map ids_; + std::map ids_; vector_type uniq_; }; diff --git a/tools/sprite_dumper/tileset_dumper.cpp b/tools/sprite_dumper/tileset_dumper.cpp index f09fbd9..4ca0785 100644 --- a/tools/sprite_dumper/tileset_dumper.cpp +++ b/tools/sprite_dumper/tileset_dumper.cpp @@ -104,111 +104,196 @@ std::vector> GetMapLayers(BufferView m3, int roomId) { return layers; } -using metatile_id = identifier::key_type; - struct TileUse { - metatile_id id; + size_t id; bool tflipx = false; bool tflipy = false; }; -Magick::Image renderTile(unsigned short ch, bool tflipx, bool tflipy, const std::vector& palettes, const std::vector& mapTiles, const std::vector>& tilesets) { - Magick::Image result("16x16", "transparent"); +unsigned short stripFlipInfo(unsigned short ch) { + return ch & ~(0x4000 | 0x8000); +} - unsigned short tile16 = ch & 0x3FF; - if ((tile16 >> 6) >= 12) return result; +class Map { +public: + + Map( + BufferView m3, + int roomNum, + RoomInfo roomInfo, + RoomGfxPal roomGfxPal) + : roomNum_(roomNum), + width_(roomInfo.width), + height_(roomInfo.height), + palettes_(roomGfxPal.GetPalettes(m3)), + tilesets_(roomGfxPal.GetTilesets(m3)), + mapTiles_(GetMapTiles(m3, roomNum)), + mapLayers_(GetMapLayers(m3, roomNum)) + { + for (int layer=0; layer& ml = mapLayers_[layer]; + if (ml.empty()) continue; + + std::vector newLayer; + + for (int mapy = 0; mapy < height_; mapy++) { + for (int mapx = 0; mapx < width_; mapx++) { + unsigned short ch = BufferView(ml).ReadTwoBytes((mapx + (mapy * width_)) * 2); + TileUse tu; + tu.id = metatiles_.add(stripFlipInfo(ch)); + tu.tflipx = (ch & 0x4000) != 0; + tu.tflipy = (ch & 0x8000) != 0; + newLayer.push_back(std::move(tu)); + } + } - result.modifyImage(); - Magick::Pixels view(result); + itemised_.push_back(std::move(newLayer)); + } - int tpal = (ch >> 10) & 0xF; - const Palette& palette = palettes[tpal]; + // Debug info + std::cout << width_ << "," << height_ << std::endl; + std::cout << roomGfxPal.paletteId; + for (int i=0;i<12;i++) std::cout << "," << roomGfxPal.tilesetId[i]; + std::cout << std::endl; + } -// bool tflipx = tu.tflipx;//(ch & 0x4000) != 0; -// bool tflipy = tu.tflipy;//(ch & 0x8000) != 0; + Magick::Image renderTile(size_t metatile_id, bool tflipx, bool tflipy) const { + Magick::Image result("16x16", "transparent"); - int tile8[2][2]; - bool sflipx[2][2]; - bool sflipy[2][2]; + unsigned short ch = metatiles_.get(metatile_id); + unsigned short tile16 = ch & 0x3FF; + if ((tile16 >> 6) >= 12) return result; - unsigned int magic = BufferView(mapTiles).ReadFourBytes(tile16 * 8); + result.modifyImage(); + Magick::Pixels view(result); - tile8[0][0] = mapTiles[(tile16 * 8) + 4]; - tile8[0][1] = mapTiles[(tile16 * 8) + 5]; - tile8[1][0] = mapTiles[(tile16 * 8) + 6]; - tile8[1][1] = mapTiles[(tile16 * 8) + 7]; + int tpal = (ch >> 10) & 0xF; + const Palette& palette = palettes_[tpal]; - for (int i=0; i<2; i++) { - for (int j=0; j<2; j++) { - sflipx[i][j] = (tile8[i][j] & 0x40) != 0; - sflipy[i][j] = (tile8[i][j] & 0x80) != 0; + int tile8[2][2]; + bool sflipx[2][2]; + bool sflipy[2][2]; - tile8[i][j] &= 0x3f; - tile8[i][j] |= (ch & 0x3c0); + unsigned int magic = BufferView(mapTiles_).ReadFourBytes(tile16 * 8); + + tile8[0][0] = mapTiles_[(tile16 * 8) + 4]; + tile8[0][1] = mapTiles_[(tile16 * 8) + 5]; + tile8[1][0] = mapTiles_[(tile16 * 8) + 6]; + tile8[1][1] = mapTiles_[(tile16 * 8) + 7]; + + for (int i=0; i<2; i++) { + for (int j=0; j<2; j++) { + sflipx[i][j] = (tile8[i][j] & 0x40) != 0; + sflipy[i][j] = (tile8[i][j] & 0x80) != 0; + + tile8[i][j] &= 0x3f; + tile8[i][j] |= (ch & 0x3c0); + } } - } - unsigned int mask = (magic >> 16) & 0xf; - if ((mask & 0x1) == 0) tile8[0][0] = -1; - if ((mask & 0x2) == 0) tile8[0][1] = -1; - if ((mask & 0x4) == 0) tile8[1][0] = -1; - if ((mask & 0x8) == 0) tile8[1][1] = -1; - - for (int tiley=0; tiley<2; tiley++) { - for (int tilex=0; tilex<2; tilex++) { - if (tile8[tiley][tilex] < 0) continue; - - int tileset = tile8[tiley][tilex] >> 6; - int subtile = tile8[tiley][tilex] & 0x3f; - - int tileData[8][8]; - BufferView tilesetData(tilesets[tileset]); - tilesetData.Seek(subtile << 5); - for (int ty=0; ty<8; ty++) { - for (int tx=0; tx<4; tx++) { - unsigned char vvvv = tilesetData.ReadNextByte(); - tileData[tx*2][ty] = static_cast(vvvv & 0xF); - tileData[tx*2+1][ty] = static_cast((vvvv >> 4) & 0xF); + unsigned int mask = (magic >> 16) & 0xf; + if ((mask & 0x1) == 0) tile8[0][0] = -1; + if ((mask & 0x2) == 0) tile8[0][1] = -1; + if ((mask & 0x4) == 0) tile8[1][0] = -1; + if ((mask & 0x8) == 0) tile8[1][1] = -1; + + for (int tiley=0; tiley<2; tiley++) { + for (int tilex=0; tilex<2; tilex++) { + if (tile8[tiley][tilex] < 0) continue; + + int tileset = tile8[tiley][tilex] >> 6; + int subtile = tile8[tiley][tilex] & 0x3f; + + int tileData[8][8]; + BufferView tilesetData(tilesets_[tileset]); + tilesetData.Seek(subtile << 5); + for (int ty=0; ty<8; ty++) { + for (int tx=0; tx<4; tx++) { + unsigned char vvvv = tilesetData.ReadNextByte(); + tileData[tx*2][ty] = static_cast(vvvv & 0xF); + tileData[tx*2+1][ty] = static_cast((vvvv >> 4) & 0xF); + } } - } - int stx = tflipx ? 1 - tilex : tilex; - int sty = tflipy ? 1 - tiley : tiley; + int stx = tflipx ? 1 - tilex : tilex; + int sty = tflipy ? 1 - tiley : tiley; - int destX = /*(mapx << 4) +*/ (stx << 3); - int destY = /*(mapy << 4) +*/ (sty << 3); + int destX = /*(mapx << 4) +*/ (stx << 3); + int destY = /*(mapy << 4) +*/ (sty << 3); - bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx); - bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy); + bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx); + bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy); - Magick::PixelPacket* pixels = view.get(destX,destY,8,8); + Magick::PixelPacket* pixels = view.get(destX,destY,8,8); - for (int ty=0; ty<8; ty++) { - int actualTy = reallyFlipY ? (7-ty) : ty; + for (int ty=0; ty<8; ty++) { + int actualTy = reallyFlipY ? (7-ty) : ty; - for (int tx=0; tx<8; tx++) { - int actualTx = reallyFlipX ? (7-tx) : tx; + for (int tx=0; tx<8; tx++) { + int actualTx = reallyFlipX ? (7-tx) : tx; - if (tileData[actualTx][actualTy] != 0) { - //auto& c = palette.Colors().at(tileData[actualTx][actualTy]); - //std::cout << c.redQuantum() << "," << c.greenQuantum() << "," << c.blueQuantum() << std::endl; - *pixels = palette.Colors().at(tileData[actualTx][actualTy]); - //std::cout << tileData[actualTx][actualTy] << std::endl; + if (tileData[actualTx][actualTy] != 0) { + //auto& c = palette.Colors().at(tileData[actualTx][actualTy]); + //std::cout << c.redQuantum() << "," << c.greenQuantum() << "," << c.blueQuantum() << std::endl; + *pixels = palette.Colors().at(tileData[actualTx][actualTy]); + //std::cout << tileData[actualTx][actualTy] << std::endl; + } + pixels++; } - pixels++; } - } - view.sync(); + view.sync(); + } } + + return result; } - return result; -} + const std::vector>& getItemisedLayers() const { + return itemised_; + } -unsigned short stripFlipInfo(unsigned short ch) { - return ch & ~(0x4000 | 0x8000); -} + int getWidth() const { return width_; } + + int getHeight() const { return height_; } + + int getRoomNum() const { return roomNum_; } + +private: + + int roomNum_; + int width_; + int height_; + std::vector palettes_; + std::vector> tilesets_; + std::vector mapTiles_; + std::vector> mapLayers_; + + identifier metatiles_; + std::vector> itemised_; +}; + +struct GlobalTile { + Magick::Image image; + std::string base64; + + GlobalTile(Magick::Image input) : image(input) { + Magick::Blob blob; + input.write(&blob); + base64 = blob.base64(); + } +}; + +class GlobalTileKeyExtract { +public: + + const std::string& operator()(const GlobalTile& globaltile) const { + return globaltile.base64; + } +}; + +using globaltile_identifier = identifier; +using globaltile_id = globaltile_identifier::id_type; int main(int argc, char** argv) { if (argc < 3) { @@ -222,125 +307,102 @@ int main(int argc, char** argv) { auto roomInfos = RoomInfo::ReadFromRom(m3.buffer()); auto roomGfxPals = RoomGfxPal::ReadFromRom(m3.buffer()); - int roomNum = std::stoi(argv[2]); - - std::cout << roomInfos[roomNum].width << "," << roomInfos[roomNum].height << std::endl; - std::cout << roomGfxPals[roomNum].paletteId; - for (int i=0;i<12;i++) std::cout << "," << roomGfxPals[roomNum].tilesetId[i]; - std::cout << std::endl; + std::list maps; + for (int i=2; i translatedTileIds; - Magick::Image image(Magick::Geometry(width*16, height*16), "transparent"); + // Generate map datafile. + std::ofstream mapfile("out" + std::to_string(map.getRoomNum()) + ".tmx"); + mapfile << R"()" << std::endl; + mapfile << R"( )" << std::endl; + + for (int layer = map.getItemisedLayers().size()-1; layer >= 0; layer--) { + mapfile << R"( )" << std::endl; + mapfile << R"( )"; + + bool first = true; + for (const TileUse& tu : map.getItemisedLayers()[layer]) { + if (first) { + first = false; + } else { + mapfile << ","; + } - auto mapTiles = GetMapTiles(m3.buffer(), roomNum); - auto mapLayers = GetMapLayers(m3.buffer(), roomNum); + if (!translatedTileIds.count(tu.id)) { + Magick::Image renderedTile = map.renderTile(tu.id, false, false); + renderedTile.magick("png"); - const auto& gfxPal = roomGfxPals[roomNum]; - auto palettes = gfxPal.GetPalettes(m3.buffer()); - auto tilesets = gfxPal.GetTilesets(m3.buffer()); + GlobalTile gt(std::move(renderedTile)); + translatedTileIds[tu.id] = globaltiles.add(gt); + } - identifier metatiles; - std::vector> itemised; + unsigned int outChar = translatedTileIds[tu.id] + 1; + if (tu.tflipx) outChar |= 0x80000000; + if (tu.tflipy) outChar |= 0x40000000; + mapfile << outChar; + } - for (int layer=0; layer& ml = mapLayers[layer]; - if (ml.empty()) continue; + mapfile << R"()" << std::endl; + mapfile << R"( )" << std::endl; + } - std::vector newLayer; + mapfile << R"()" << std::endl; - for (int mapy = 0; mapy < height; mapy++) { - for (int mapx = 0; mapx < width; mapx++) { - unsigned short ch = BufferView(ml).ReadTwoBytes((mapx + (mapy * width)) * 2); - TileUse tu; - tu.id = metatiles.add(stripFlipInfo(ch)); - tu.tflipx = (ch & 0x4000) != 0; - tu.tflipy = (ch & 0x8000) != 0; - newLayer.push_back(std::move(tu)); + // Render map to image. + /*for (int layer=itemised.size()-1; layer>=0; layer--) { + for (int mapy = 0; mapy < height; mapy++) { + for (int mapx = 0; mapx < width; mapx++) { + const TileUse& tu = itemised[layer][mapx+mapy*width]; + Magick::Image tileRender = renderTile(metatiles.get(tu.id), tu.tflipx, tu.tflipy, palettes, mapTiles, tilesets); + image.composite(tileRender, mapx << 4, mapy << 4, Magick::OverCompositeOp); + } } - } - - itemised.push_back(std::move(newLayer)); + }*/ } constexpr int TILES_PER_ROW = 10; int sheetWidth; int sheetHeight; - if (metatiles.size() < TILES_PER_ROW) { - sheetWidth = metatiles.size() * 16; + if (globaltiles.size() < TILES_PER_ROW) { + sheetWidth = globaltiles.size() * 16; sheetHeight = 16; } else { sheetWidth = TILES_PER_ROW * 16; - sheetHeight = (metatiles.size() / TILES_PER_ROW + 1) * 16; - } - - // Generate map datafile. - std::ofstream mapfile("out.tmx"); - mapfile << R"()" << std::endl; - mapfile << R"( )" << std::endl; - mapfile << R"( )" << std::endl; - mapfile << R"( )" << std::endl; - - for (int layer=itemised.size()-1; layer>=0; layer--) { - mapfile << R"( )" << std::endl; - mapfile << R"( )"; - - bool first = true; - for (const TileUse& tu : itemised[layer]) { - if (first) { - first = false; - } else { - mapfile << ","; - } - - unsigned int outChar = tu.id + 1; - if (tu.tflipx) outChar |= 0x80000000; - if (tu.tflipy) outChar |= 0x40000000; - mapfile << outChar; - } - - mapfile << R"()" << std::endl; - mapfile << R"( )" << std::endl; - } - - mapfile << R"()" << std::endl; - - // Render map to image. - for (int layer=itemised.size()-1; layer>=0; layer--) { - for (int mapy = 0; mapy < height; mapy++) { - for (int mapx = 0; mapx < width; mapx++) { - const TileUse& tu = itemised[layer][mapx+mapy*width]; - Magick::Image tileRender = renderTile(metatiles.get(tu.id), tu.tflipx, tu.tflipy, palettes, mapTiles, tilesets); - image.composite(tileRender, mapx << 4, mapy << 4, Magick::OverCompositeOp); - } - } + sheetHeight = (globaltiles.size() / TILES_PER_ROW + 1) * 16; } - image.magick("png"); - image.write("out.png"); + std::ofstream tilesetfile("out.tsx"); + tilesetfile << R"()" << std::endl; + tilesetfile << R"( )" << std::endl; + tilesetfile << R"()" << std::endl; // Render tileset image. Magick::Image tilesetImage(Magick::Geometry(sheetWidth, sheetHeight), "transparent"); - for (int i=0; i