diff options
| author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2021-02-01 13:14:40 -0500 |
|---|---|---|
| committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2021-02-01 13:14:40 -0500 |
| commit | 362c332000a65acc060660dcb6bb0ec6f99cbafe (patch) | |
| tree | b8f64b23c45e1e016ef708421830e83a0b4bc63c /tools/sprite_dumper/tileset_dumper.cpp | |
| parent | e37026ea3b9d7b121aeb1fd38fe81ab82a1e2e33 (diff) | |
| download | tanetane-362c332000a65acc060660dcb6bb0ec6f99cbafe.tar.gz tanetane-362c332000a65acc060660dcb6bb0ec6f99cbafe.tar.bz2 tanetane-362c332000a65acc060660dcb6bb0ec6f99cbafe.zip | |
Tileset dumper can now export to Tiled
Diffstat (limited to 'tools/sprite_dumper/tileset_dumper.cpp')
| -rw-r--r-- | tools/sprite_dumper/tileset_dumper.cpp | 275 |
1 files changed, 195 insertions, 80 deletions
| 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 @@ | |||
| 3 | #include <vector> | 3 | #include <vector> |
| 4 | #include <string> | 4 | #include <string> |
| 5 | #include <map> | 5 | #include <map> |
| 6 | #include <fstream> | ||
| 6 | #include "common.h" | 7 | #include "common.h" |
| 8 | #include "identifier.h" | ||
| 7 | 9 | ||
| 8 | constexpr int NUM_ROOMS = 1000; | 10 | constexpr int NUM_ROOMS = 1000; |
| 9 | constexpr int NUM_TILESETS = 12; | 11 | constexpr int NUM_TILESETS = 12; |
| @@ -102,6 +104,112 @@ std::vector<std::vector<char>> GetMapLayers(BufferView m3, int roomId) { | |||
| 102 | return layers; | 104 | return layers; |
| 103 | } | 105 | } |
| 104 | 106 | ||
| 107 | using metatile_id = identifier<unsigned short>::key_type; | ||
| 108 | |||
| 109 | struct TileUse { | ||
| 110 | metatile_id id; | ||
| 111 | bool tflipx = false; | ||
| 112 | bool tflipy = false; | ||
| 113 | }; | ||
| 114 | |||
| 115 | Magick::Image renderTile(unsigned short ch, bool tflipx, bool tflipy, const std::vector<Palette>& palettes, const std::vector<char>& mapTiles, const std::vector<std::vector<char>>& tilesets) { | ||
| 116 | Magick::Image result("16x16", "transparent"); | ||
| 117 | |||
| 118 | unsigned short tile16 = ch & 0x3FF; | ||
| 119 | if ((tile16 >> 6) >= 12) return result; | ||
| 120 | |||
| 121 | result.modifyImage(); | ||
| 122 | Magick::Pixels view(result); | ||
| 123 | |||
| 124 | int tpal = (ch >> 10) & 0xF; | ||
| 125 | const Palette& palette = palettes[tpal]; | ||
| 126 | |||
| 127 | // bool tflipx = tu.tflipx;//(ch & 0x4000) != 0; | ||
| 128 | // bool tflipy = tu.tflipy;//(ch & 0x8000) != 0; | ||
| 129 | |||
| 130 | int tile8[2][2]; | ||
| 131 | bool sflipx[2][2]; | ||
| 132 | bool sflipy[2][2]; | ||
| 133 | |||
| 134 | unsigned int magic = BufferView(mapTiles).ReadFourBytes(tile16 * 8); | ||
| 135 | |||
| 136 | tile8[0][0] = mapTiles[(tile16 * 8) + 4]; | ||
| 137 | tile8[0][1] = mapTiles[(tile16 * 8) + 5]; | ||
| 138 | tile8[1][0] = mapTiles[(tile16 * 8) + 6]; | ||
| 139 | tile8[1][1] = mapTiles[(tile16 * 8) + 7]; | ||
| 140 | |||
| 141 | for (int i=0; i<2; i++) { | ||
| 142 | for (int j=0; j<2; j++) { | ||
| 143 | sflipx[i][j] = (tile8[i][j] & 0x40) != 0; | ||
| 144 | sflipy[i][j] = (tile8[i][j] & 0x80) != 0; | ||
| 145 | |||
| 146 | tile8[i][j] &= 0x3f; | ||
| 147 | tile8[i][j] |= (ch & 0x3c0); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | unsigned int mask = (magic >> 16) & 0xf; | ||
| 152 | if ((mask & 0x1) == 0) tile8[0][0] = -1; | ||
| 153 | if ((mask & 0x2) == 0) tile8[0][1] = -1; | ||
| 154 | if ((mask & 0x4) == 0) tile8[1][0] = -1; | ||
| 155 | if ((mask & 0x8) == 0) tile8[1][1] = -1; | ||
| 156 | |||
| 157 | for (int tiley=0; tiley<2; tiley++) { | ||
| 158 | for (int tilex=0; tilex<2; tilex++) { | ||
| 159 | if (tile8[tiley][tilex] < 0) continue; | ||
| 160 | |||
| 161 | int tileset = tile8[tiley][tilex] >> 6; | ||
| 162 | int subtile = tile8[tiley][tilex] & 0x3f; | ||
| 163 | |||
| 164 | int tileData[8][8]; | ||
| 165 | BufferView tilesetData(tilesets[tileset]); | ||
| 166 | tilesetData.Seek(subtile << 5); | ||
| 167 | for (int ty=0; ty<8; ty++) { | ||
| 168 | for (int tx=0; tx<4; tx++) { | ||
| 169 | unsigned char vvvv = tilesetData.ReadNextByte(); | ||
| 170 | tileData[tx*2][ty] = static_cast<unsigned char>(vvvv & 0xF); | ||
| 171 | tileData[tx*2+1][ty] = static_cast<unsigned char>((vvvv >> 4) & 0xF); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | int stx = tflipx ? 1 - tilex : tilex; | ||
| 176 | int sty = tflipy ? 1 - tiley : tiley; | ||
| 177 | |||
| 178 | int destX = /*(mapx << 4) +*/ (stx << 3); | ||
| 179 | int destY = /*(mapy << 4) +*/ (sty << 3); | ||
| 180 | |||
| 181 | bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx); | ||
| 182 | bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy); | ||
| 183 | |||
| 184 | Magick::PixelPacket* pixels = view.get(destX,destY,8,8); | ||
| 185 | |||
| 186 | for (int ty=0; ty<8; ty++) { | ||
| 187 | int actualTy = reallyFlipY ? (7-ty) : ty; | ||
| 188 | |||
| 189 | for (int tx=0; tx<8; tx++) { | ||
| 190 | int actualTx = reallyFlipX ? (7-tx) : tx; | ||
| 191 | |||
| 192 | if (tileData[actualTx][actualTy] != 0) { | ||
| 193 | //auto& c = palette.Colors().at(tileData[actualTx][actualTy]); | ||
| 194 | //std::cout << c.redQuantum() << "," << c.greenQuantum() << "," << c.blueQuantum() << std::endl; | ||
| 195 | *pixels = palette.Colors().at(tileData[actualTx][actualTy]); | ||
| 196 | //std::cout << tileData[actualTx][actualTy] << std::endl; | ||
| 197 | } | ||
| 198 | pixels++; | ||
| 199 | } | ||
| 200 | } | ||
| 201 | |||
| 202 | view.sync(); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | return result; | ||
| 207 | } | ||
| 208 | |||
| 209 | unsigned short stripFlipInfo(unsigned short ch) { | ||
| 210 | return ch & ~(0x4000 | 0x8000); | ||
| 211 | } | ||
| 212 | |||
| 105 | int main(int argc, char** argv) { | 213 | int main(int argc, char** argv) { |
| 106 | if (argc < 3) { | 214 | if (argc < 3) { |
| 107 | std::cout << "Usage: ./tileset_dumper [path to rom] {map ID}" << std::endl; | 215 | std::cout << "Usage: ./tileset_dumper [path to rom] {map ID}" << std::endl; |
| @@ -125,8 +233,6 @@ int main(int argc, char** argv) { | |||
| 125 | int height = roomInfos[roomNum].height; | 233 | int height = roomInfos[roomNum].height; |
| 126 | 234 | ||
| 127 | Magick::Image image(Magick::Geometry(width*16, height*16), "transparent"); | 235 | Magick::Image image(Magick::Geometry(width*16, height*16), "transparent"); |
| 128 | image.modifyImage(); | ||
| 129 | Magick::Pixels view(image); | ||
| 130 | 236 | ||
| 131 | auto mapTiles = GetMapTiles(m3.buffer(), roomNum); | 237 | auto mapTiles = GetMapTiles(m3.buffer(), roomNum); |
| 132 | auto mapLayers = GetMapLayers(m3.buffer(), roomNum); | 238 | auto mapLayers = GetMapLayers(m3.buffer(), roomNum); |
| @@ -135,102 +241,111 @@ int main(int argc, char** argv) { | |||
| 135 | auto palettes = gfxPal.GetPalettes(m3.buffer()); | 241 | auto palettes = gfxPal.GetPalettes(m3.buffer()); |
| 136 | auto tilesets = gfxPal.GetTilesets(m3.buffer()); | 242 | auto tilesets = gfxPal.GetTilesets(m3.buffer()); |
| 137 | 243 | ||
| 138 | for (int layer=mapLayers.size()-1; layer>=0; layer--) { | 244 | identifier<unsigned short> metatiles; |
| 245 | std::vector<std::vector<TileUse>> itemised; | ||
| 246 | |||
| 247 | for (int layer=0; layer<mapLayers.size(); layer++) { | ||
| 139 | const std::vector<char>& ml = mapLayers[layer]; | 248 | const std::vector<char>& ml = mapLayers[layer]; |
| 140 | if (ml.empty()) continue; | 249 | if (ml.empty()) continue; |
| 141 | 250 | ||
| 251 | std::vector<TileUse> newLayer; | ||
| 252 | |||
| 142 | for (int mapy = 0; mapy < height; mapy++) { | 253 | for (int mapy = 0; mapy < height; mapy++) { |
| 143 | for (int mapx = 0; mapx < width; mapx++) { | 254 | for (int mapx = 0; mapx < width; mapx++) { |
| 144 | unsigned short ch = BufferView(ml).ReadTwoBytes((mapx + (mapy * width)) * 2); | 255 | unsigned short ch = BufferView(ml).ReadTwoBytes((mapx + (mapy * width)) * 2); |
| 145 | unsigned short tile16 = ch & 0x3FF; | 256 | TileUse tu; |
| 146 | 257 | tu.id = metatiles.add(stripFlipInfo(ch)); | |
| 147 | if ((tile16 >> 6) >= 12) continue; | 258 | tu.tflipx = (ch & 0x4000) != 0; |
| 259 | tu.tflipy = (ch & 0x8000) != 0; | ||
| 260 | newLayer.push_back(std::move(tu)); | ||
| 261 | } | ||
| 262 | } | ||
| 148 | 263 | ||
| 149 | int tpal = (ch >> 10) & 0xF; | 264 | itemised.push_back(std::move(newLayer)); |
| 150 | const Palette& palette = palettes[tpal]; | 265 | } |
| 151 | 266 | ||
| 152 | bool tflipx = (ch & 0x4000) != 0; | ||
| 153 | bool tflipy = (ch & 0x8000) != 0; | ||
| 154 | 267 | ||
| 155 | int tile8[2][2]; | ||
| 156 | bool sflipx[2][2]; | ||
| 157 | bool sflipy[2][2]; | ||
| 158 | 268 | ||
| 159 | unsigned int magic = BufferView(mapTiles).ReadFourBytes(tile16 * 8); | 269 | std::ofstream mapfile("out.tmx"); |
| 270 | mapfile << R"(<map version="1.0" orientation="orthogonal" renderorder="right-down" width=")"; | ||
| 271 | mapfile << width; | ||
| 272 | mapfile << R"(" height=")"; | ||
| 273 | mapfile << height; | ||
| 274 | mapfile << R"(" tilewidth="16" tileheight="16">)" << std::endl; | ||
| 275 | mapfile << R"( <tileset firstgid="0" source="out.tsx" />)" << std::endl; | ||
| 276 | |||
| 277 | for (int layer=itemised.size()-1; layer>=0; layer--) { | ||
| 278 | mapfile << R"( <layer id=")"; | ||
| 279 | mapfile << layer; | ||
| 280 | mapfile << R"(" name="Layer )"; | ||
| 281 | mapfile << layer; | ||
| 282 | mapfile << R"(" width=")"; | ||
| 283 | mapfile << width; | ||
| 284 | mapfile << R"(" height=")"; | ||
| 285 | mapfile << height; | ||
| 286 | mapfile << R"(">)" << std::endl; | ||
| 287 | mapfile << R"( <data encoding="csv">)"; | ||
| 288 | |||
| 289 | bool first = true; | ||
| 290 | for (const TileUse& tu : itemised[layer]) { | ||
| 291 | if (first) { | ||
| 292 | first = false; | ||
| 293 | } else { | ||
| 294 | mapfile << ","; | ||
| 295 | } | ||
| 160 | 296 | ||
| 161 | tile8[0][0] = mapTiles[(tile16 * 8) + 4]; | 297 | unsigned int outChar = tu.id; |
| 162 | tile8[0][1] = mapTiles[(tile16 * 8) + 5]; | 298 | if (tu.tflipx) outChar |= 0x80000000; |
| 163 | tile8[1][0] = mapTiles[(tile16 * 8) + 6]; | 299 | if (tu.tflipy) outChar |= 0x40000000; |
| 164 | tile8[1][1] = mapTiles[(tile16 * 8) + 7]; | 300 | mapfile << outChar; |
| 301 | } | ||
| 165 | 302 | ||
| 166 | for (int i=0; i<2; i++) { | 303 | mapfile << R"(</data>)" << std::endl; |
| 167 | for (int j=0; j<2; j++) { | 304 | mapfile << R"( </layer>)" << std::endl; |
| 168 | sflipx[i][j] = (tile8[i][j] & 0x40) != 0; | 305 | } |
| 169 | sflipy[i][j] = (tile8[i][j] & 0x80) != 0; | ||
| 170 | 306 | ||
| 171 | tile8[i][j] &= 0x3f; | 307 | mapfile << R"(</map>)" << std::endl; |
| 172 | tile8[i][j] |= (ch & 0x3c0); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | 308 | ||
| 176 | unsigned int mask = (magic >> 16) & 0xf; | 309 | for (int layer=itemised.size()-1; layer>=0; layer--) { |
| 177 | if ((mask & 0x1) == 0) tile8[0][0] = -1; | 310 | for (int mapy = 0; mapy < height; mapy++) { |
| 178 | if ((mask & 0x2) == 0) tile8[0][1] = -1; | 311 | for (int mapx = 0; mapx < width; mapx++) { |
| 179 | if ((mask & 0x4) == 0) tile8[1][0] = -1; | 312 | const TileUse& tu = itemised[layer][mapx+mapy*width]; |
| 180 | if ((mask & 0x8) == 0) tile8[1][1] = -1; | 313 | Magick::Image tileRender = renderTile(metatiles.get(tu.id), tu.tflipx, tu.tflipy, palettes, mapTiles, tilesets); |
| 181 | 314 | image.composite(tileRender, mapx << 4, mapy << 4, Magick::OverCompositeOp); | |
| 182 | for (int tiley=0; tiley<2; tiley++) { | ||
| 183 | for (int tilex=0; tilex<2; tilex++) { | ||
| 184 | if (tile8[tiley][tilex] < 0) continue; | ||
| 185 | |||
| 186 | int tileset = tile8[tiley][tilex] >> 6; | ||
| 187 | int subtile = tile8[tiley][tilex] & 0x3f; | ||
| 188 | |||
| 189 | int tileData[8][8]; | ||
| 190 | BufferView tilesetData(tilesets[tileset]); | ||
| 191 | tilesetData.Seek(subtile << 5); | ||
| 192 | for (int ty=0; ty<8; ty++) { | ||
| 193 | for (int tx=0; tx<4; tx++) { | ||
| 194 | unsigned char vvvv = tilesetData.ReadNextByte(); | ||
| 195 | tileData[tx*2][ty] = static_cast<unsigned char>(vvvv & 0xF); | ||
| 196 | tileData[tx*2+1][ty] = static_cast<unsigned char>((vvvv >> 4) & 0xF); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | int stx = tflipx ? 1 - tilex : tilex; | ||
| 201 | int sty = tflipy ? 1 - tiley : tiley; | ||
| 202 | |||
| 203 | int destX = (mapx << 4) + (stx << 3); | ||
| 204 | int destY = (mapy << 4) + (sty << 3); | ||
| 205 | |||
| 206 | bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx); | ||
| 207 | bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy); | ||
| 208 | |||
| 209 | Magick::PixelPacket* pixels = view.get(destX,destY,8,8); | ||
| 210 | |||
| 211 | for (int ty=0; ty<8; ty++) { | ||
| 212 | int actualTy = reallyFlipY ? (7-ty) : ty; | ||
| 213 | |||
| 214 | for (int tx=0; tx<8; tx++) { | ||
| 215 | int actualTx = reallyFlipX ? (7-tx) : tx; | ||
| 216 | |||
| 217 | if (tileData[actualTx][actualTy] != 0) { | ||
| 218 | //auto& c = palette.Colors().at(tileData[actualTx][actualTy]); | ||
| 219 | //std::cout << c.redQuantum() << "," << c.greenQuantum() << "," << c.blueQuantum() << std::endl; | ||
| 220 | *pixels = palette.Colors().at(tileData[actualTx][actualTy]); | ||
| 221 | //std::cout << tileData[actualTx][actualTy] << std::endl; | ||
| 222 | } | ||
| 223 | pixels++; | ||
| 224 | } | ||
| 225 | } | ||
| 226 | |||
| 227 | view.sync(); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | } | 315 | } |
| 231 | } | 316 | } |
| 232 | } | 317 | } |
| 233 | 318 | ||
| 234 | image.magick("png"); | 319 | image.magick("png"); |
| 235 | image.write("out.png"); | 320 | image.write("out.png"); |
| 321 | |||
| 322 | constexpr int TILES_PER_ROW = 10; | ||
| 323 | int sheetWidth; | ||
| 324 | int sheetHeight; | ||
| 325 | |||
| 326 | if (metatiles.size() < TILES_PER_ROW) { | ||
| 327 | sheetWidth = metatiles.size() * 16; | ||
| 328 | sheetHeight = 16; | ||
| 329 | } else { | ||
| 330 | sheetWidth = TILES_PER_ROW * 16; | ||
| 331 | sheetHeight = (metatiles.size() / TILES_PER_ROW + 1) * 16; | ||
| 332 | } | ||
| 333 | |||
| 334 | std::ofstream tilesetfile("out.tsx"); | ||
| 335 | tilesetfile << R"(<tileset name="fromRom" tilewidth="16" tileheight="16" tilecount=")"; | ||
| 336 | tilesetfile << metatiles.size(); | ||
| 337 | tilesetfile << R"(" columns=")"; | ||
| 338 | tilesetfile << TILES_PER_ROW; | ||
| 339 | tilesetfile << R"(">)" << std::endl; | ||
| 340 | tilesetfile << R"( <image source="tiles.png" />)" << std::endl; | ||
| 341 | tilesetfile << R"(</tileset>)" << std::endl; | ||
| 342 | |||
| 343 | Magick::Image tilesetImage(Magick::Geometry(sheetWidth, sheetHeight), "transparent"); | ||
| 344 | for (int i=0; i<metatiles.size(); i++) { | ||
| 345 | Magick::Image tileRender = renderTile(metatiles.get(i), false, false, palettes, mapTiles, tilesets); | ||
| 346 | tilesetImage.composite(tileRender, (i % TILES_PER_ROW) << 4, (i / TILES_PER_ROW) << 4, Magick::OverCompositeOp); | ||
| 347 | } | ||
| 348 | |||
| 349 | tilesetImage.magick("png"); | ||
| 350 | tilesetImage.write("tiles.png"); | ||
| 236 | } \ No newline at end of file | 351 | } \ No newline at end of file |
