From 362c332000a65acc060660dcb6bb0ec6f99cbafe Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Mon, 1 Feb 2021 13:14:40 -0500 Subject: Tileset dumper can now export to Tiled --- tools/sprite_dumper/identifier.h | 59 +++++++ tools/sprite_dumper/tileset_dumper.cpp | 275 +++++++++++++++++++++++---------- 2 files changed, 254 insertions(+), 80 deletions(-) create mode 100644 tools/sprite_dumper/identifier.h (limited to 'tools/sprite_dumper') diff --git a/tools/sprite_dumper/identifier.h b/tools/sprite_dumper/identifier.h new file mode 100644 index 0000000..74d83ce --- /dev/null +++ b/tools/sprite_dumper/identifier.h @@ -0,0 +1,59 @@ +#ifndef IDENTIFIER_H_D7EE5679 +#define IDENTIFIER_H_D7EE5679 + +#include +#include + +template +class identifier { +public: + + using value_type = T; + +private: + + using vector_type = std::vector; + +public: + + using key_type = typename vector_type::size_type; + + key_type add(const value_type& val) + { + auto it = ids_.find(val); + + if (it == std::end(ids_)) + { + key_type ret = ids_.size(); + ids_[val] = ret; + + uniq_.push_back(val); + + return ret; + } else { + return it->second; + } + } + + void compile() + { + ids_.clear(); + } + + inline const value_type& get(key_type i) const + { + return uniq_.at(i); + } + + inline key_type size() const + { + return uniq_.size(); + } + +private: + + std::map ids_; + vector_type uniq_; +}; + +#endif /* end of include guard: IDENTIFIER_H_D7EE5679 */ diff --git a/tools/sprite_dumper/tileset_dumper.cpp b/tools/sprite_dumper/tileset_dumper.cpp index de6e1f4..d8997a5 100644 --- a/tools/sprite_dumper/tileset_dumper.cpp +++ b/tools/sprite_dumper/tileset_dumper.cpp @@ -3,7 +3,9 @@ #include #include #include +#include #include "common.h" +#include "identifier.h" constexpr int NUM_ROOMS = 1000; constexpr int NUM_TILESETS = 12; @@ -102,6 +104,112 @@ std::vector> GetMapLayers(BufferView m3, int roomId) { return layers; } +using metatile_id = identifier::key_type; + +struct TileUse { + metatile_id 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 tile16 = ch & 0x3FF; + if ((tile16 >> 6) >= 12) return result; + + result.modifyImage(); + Magick::Pixels view(result); + + int tpal = (ch >> 10) & 0xF; + const Palette& palette = palettes[tpal]; + +// bool tflipx = tu.tflipx;//(ch & 0x4000) != 0; +// bool tflipy = tu.tflipy;//(ch & 0x8000) != 0; + + int tile8[2][2]; + bool sflipx[2][2]; + bool sflipy[2][2]; + + 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); + } + } + + int stx = tflipx ? 1 - tilex : tilex; + int sty = tflipy ? 1 - tiley : tiley; + + int destX = /*(mapx << 4) +*/ (stx << 3); + int destY = /*(mapy << 4) +*/ (sty << 3); + + bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx); + bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy); + + Magick::PixelPacket* pixels = view.get(destX,destY,8,8); + + 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; + + 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++; + } + } + + view.sync(); + } + } + + return result; +} + +unsigned short stripFlipInfo(unsigned short ch) { + return ch & ~(0x4000 | 0x8000); +} + int main(int argc, char** argv) { if (argc < 3) { std::cout << "Usage: ./tileset_dumper [path to rom] {map ID}" << std::endl; @@ -125,8 +233,6 @@ int main(int argc, char** argv) { int height = roomInfos[roomNum].height; Magick::Image image(Magick::Geometry(width*16, height*16), "transparent"); - image.modifyImage(); - Magick::Pixels view(image); auto mapTiles = GetMapTiles(m3.buffer(), roomNum); auto mapLayers = GetMapLayers(m3.buffer(), roomNum); @@ -135,102 +241,111 @@ int main(int argc, char** argv) { auto palettes = gfxPal.GetPalettes(m3.buffer()); auto tilesets = gfxPal.GetTilesets(m3.buffer()); - for (int layer=mapLayers.size()-1; layer>=0; layer--) { + identifier metatiles; + std::vector> itemised; + + 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); - unsigned short tile16 = ch & 0x3FF; - - if ((tile16 >> 6) >= 12) continue; + TileUse tu; + tu.id = metatiles.add(stripFlipInfo(ch)); + tu.tflipx = (ch & 0x4000) != 0; + tu.tflipy = (ch & 0x8000) != 0; + newLayer.push_back(std::move(tu)); + } + } - int tpal = (ch >> 10) & 0xF; - const Palette& palette = palettes[tpal]; + itemised.push_back(std::move(newLayer)); + } - bool tflipx = (ch & 0x4000) != 0; - bool tflipy = (ch & 0x8000) != 0; - int tile8[2][2]; - bool sflipx[2][2]; - bool sflipy[2][2]; - unsigned int magic = BufferView(mapTiles).ReadFourBytes(tile16 * 8); + std::ofstream mapfile("out.tmx"); + 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 << ","; + } - 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]; + unsigned int outChar = tu.id; + if (tu.tflipx) outChar |= 0x80000000; + if (tu.tflipy) outChar |= 0x40000000; + mapfile << outChar; + } - 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; + mapfile << R"()" << std::endl; + mapfile << R"( )" << std::endl; + } - tile8[i][j] &= 0x3f; - tile8[i][j] |= (ch & 0x3c0); - } - } + mapfile << R"()" << std::endl; - 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 destX = (mapx << 4) + (stx << 3); - int destY = (mapy << 4) + (sty << 3); - - bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx); - bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy); - - Magick::PixelPacket* pixels = view.get(destX,destY,8,8); - - 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; - - 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++; - } - } - - view.sync(); - } - } + 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); } } } image.magick("png"); image.write("out.png"); + + constexpr int TILES_PER_ROW = 10; + int sheetWidth; + int sheetHeight; + + if (metatiles.size() < TILES_PER_ROW) { + sheetWidth = metatiles.size() * 16; + sheetHeight = 16; + } else { + sheetWidth = TILES_PER_ROW * 16; + sheetHeight = (metatiles.size() / TILES_PER_ROW + 1) * 16; + } + + std::ofstream tilesetfile("out.tsx"); + tilesetfile << R"()" << std::endl; + tilesetfile << R"( )" << std::endl; + tilesetfile << R"()" << std::endl; + + Magick::Image tilesetImage(Magick::Geometry(sheetWidth, sheetHeight), "transparent"); + for (int i=0; i