summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKelly Rauchenberger <fefferburbia@gmail.com>2021-02-01 13:14:40 -0500
committerKelly Rauchenberger <fefferburbia@gmail.com>2021-02-01 13:14:40 -0500
commit362c332000a65acc060660dcb6bb0ec6f99cbafe (patch)
treeb8f64b23c45e1e016ef708421830e83a0b4bc63c
parente37026ea3b9d7b121aeb1fd38fe81ab82a1e2e33 (diff)
downloadtanetane-362c332000a65acc060660dcb6bb0ec6f99cbafe.tar.gz
tanetane-362c332000a65acc060660dcb6bb0ec6f99cbafe.tar.bz2
tanetane-362c332000a65acc060660dcb6bb0ec6f99cbafe.zip
Tileset dumper can now export to Tiled
-rw-r--r--tools/sprite_dumper/identifier.h59
-rw-r--r--tools/sprite_dumper/tileset_dumper.cpp275
2 files changed, 254 insertions, 80 deletions
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 @@
1#ifndef IDENTIFIER_H_D7EE5679
2#define IDENTIFIER_H_D7EE5679
3
4#include <map>
5#include <vector>
6
7template <typename T>
8class identifier {
9public:
10
11 using value_type = T;
12
13private:
14
15 using vector_type = std::vector<value_type>;
16
17public:
18
19 using key_type = typename vector_type::size_type;
20
21 key_type add(const value_type& val)
22 {
23 auto it = ids_.find(val);
24
25 if (it == std::end(ids_))
26 {
27 key_type ret = ids_.size();
28 ids_[val] = ret;
29
30 uniq_.push_back(val);
31
32 return ret;
33 } else {
34 return it->second;
35 }
36 }
37
38 void compile()
39 {
40 ids_.clear();
41 }
42
43 inline const value_type& get(key_type i) const
44 {
45 return uniq_.at(i);
46 }
47
48 inline key_type size() const
49 {
50 return uniq_.size();
51 }
52
53private:
54
55 std::map<value_type, key_type> ids_;
56 vector_type uniq_;
57};
58
59#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 @@
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
8constexpr int NUM_ROOMS = 1000; 10constexpr int NUM_ROOMS = 1000;
9constexpr int NUM_TILESETS = 12; 11constexpr 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
107using metatile_id = identifier<unsigned short>::key_type;
108
109struct TileUse {
110 metatile_id id;
111 bool tflipx = false;
112 bool tflipy = false;
113};
114
115Magick::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
209unsigned short stripFlipInfo(unsigned short ch) {
210 return ch & ~(0x4000 | 0x8000);
211}
212
105int main(int argc, char** argv) { 213int 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