summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--tools/sprite_dumper/identifier.h26
-rw-r--r--tools/sprite_dumper/tileset_dumper.cpp402
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 @@
5#include <vector> 5#include <vector>
6 6
7template <typename T> 7template <typename T>
8class DefaultKeyExtract {
9public:
10
11 const T& operator()(const T& val) const {
12 return val;
13 }
14};
15
16template <typename T, typename KeyExtract = DefaultKeyExtract<T>>
8class identifier { 17class identifier {
9public: 18public:
10 19
@@ -12,20 +21,21 @@ public:
12 21
13private: 22private:
14 23
24 using key_type = std::remove_reference_t<std::invoke_result_t<KeyExtract, const T&>>;
15 using vector_type = std::vector<value_type>; 25 using vector_type = std::vector<value_type>;
16 26
17public: 27public:
18 28
19 using key_type = typename vector_type::size_type; 29 using id_type = typename vector_type::size_type;
20 30
21 key_type add(const value_type& val) 31 id_type add(const value_type& val)
22 { 32 {
23 auto it = ids_.find(val); 33 auto it = ids_.find(KeyExtract()(val));
24 34
25 if (it == std::end(ids_)) 35 if (it == std::end(ids_))
26 { 36 {
27 key_type ret = ids_.size(); 37 id_type ret = ids_.size();
28 ids_[val] = ret; 38 ids_[KeyExtract()(val)] = ret;
29 39
30 uniq_.push_back(val); 40 uniq_.push_back(val);
31 41
@@ -40,19 +50,19 @@ public:
40 ids_.clear(); 50 ids_.clear();
41 } 51 }
42 52
43 inline const value_type& get(key_type i) const 53 inline const value_type& get(id_type i) const
44 { 54 {
45 return uniq_.at(i); 55 return uniq_.at(i);
46 } 56 }
47 57
48 inline key_type size() const 58 inline id_type size() const
49 { 59 {
50 return uniq_.size(); 60 return uniq_.size();
51 } 61 }
52 62
53private: 63private:
54 64
55 std::map<value_type, key_type> ids_; 65 std::map<key_type, id_type> ids_;
56 vector_type uniq_; 66 vector_type uniq_;
57}; 67};
58 68
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<std::vector<char>> GetMapLayers(BufferView m3, int roomId) {
104 return layers; 104 return layers;
105} 105}
106 106
107using metatile_id = identifier<unsigned short>::key_type;
108
109struct TileUse { 107struct TileUse {
110 metatile_id id; 108 size_t id;
111 bool tflipx = false; 109 bool tflipx = false;
112 bool tflipy = false; 110 bool tflipy = false;
113}; 111};
114 112
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) { 113unsigned short stripFlipInfo(unsigned short ch) {
116 Magick::Image result("16x16", "transparent"); 114 return ch & ~(0x4000 | 0x8000);
115}
117 116
118 unsigned short tile16 = ch & 0x3FF; 117class Map {
119 if ((tile16 >> 6) >= 12) return result; 118public:
119
120 Map(
121 BufferView m3,
122 int roomNum,
123 RoomInfo roomInfo,
124 RoomGfxPal roomGfxPal)
125 : roomNum_(roomNum),
126 width_(roomInfo.width),
127 height_(roomInfo.height),
128 palettes_(roomGfxPal.GetPalettes(m3)),
129 tilesets_(roomGfxPal.GetTilesets(m3)),
130 mapTiles_(GetMapTiles(m3, roomNum)),
131 mapLayers_(GetMapLayers(m3, roomNum))
132 {
133 for (int layer=0; layer<mapLayers_.size(); layer++) {
134 const std::vector<char>& ml = mapLayers_[layer];
135 if (ml.empty()) continue;
136
137 std::vector<TileUse> newLayer;
138
139 for (int mapy = 0; mapy < height_; mapy++) {
140 for (int mapx = 0; mapx < width_; mapx++) {
141 unsigned short ch = BufferView(ml).ReadTwoBytes((mapx + (mapy * width_)) * 2);
142 TileUse tu;
143 tu.id = metatiles_.add(stripFlipInfo(ch));
144 tu.tflipx = (ch & 0x4000) != 0;
145 tu.tflipy = (ch & 0x8000) != 0;
146 newLayer.push_back(std::move(tu));
147 }
148 }
120 149
121 result.modifyImage(); 150 itemised_.push_back(std::move(newLayer));
122 Magick::Pixels view(result); 151 }
123 152
124 int tpal = (ch >> 10) & 0xF; 153 // Debug info
125 const Palette& palette = palettes[tpal]; 154 std::cout << width_ << "," << height_ << std::endl;
155 std::cout << roomGfxPal.paletteId;
156 for (int i=0;i<12;i++) std::cout << "," << roomGfxPal.tilesetId[i];
157 std::cout << std::endl;
158 }
126 159
127// bool tflipx = tu.tflipx;//(ch & 0x4000) != 0; 160 Magick::Image renderTile(size_t metatile_id, bool tflipx, bool tflipy) const {
128// bool tflipy = tu.tflipy;//(ch & 0x8000) != 0; 161 Magick::Image result("16x16", "transparent");
129 162
130 int tile8[2][2]; 163 unsigned short ch = metatiles_.get(metatile_id);
131 bool sflipx[2][2]; 164 unsigned short tile16 = ch & 0x3FF;
132 bool sflipy[2][2]; 165 if ((tile16 >> 6) >= 12) return result;
133 166
134 unsigned int magic = BufferView(mapTiles).ReadFourBytes(tile16 * 8); 167 result.modifyImage();
168 Magick::Pixels view(result);
135 169
136 tile8[0][0] = mapTiles[(tile16 * 8) + 4]; 170 int tpal = (ch >> 10) & 0xF;
137 tile8[0][1] = mapTiles[(tile16 * 8) + 5]; 171 const Palette& palette = palettes_[tpal];
138 tile8[1][0] = mapTiles[(tile16 * 8) + 6];
139 tile8[1][1] = mapTiles[(tile16 * 8) + 7];
140 172
141 for (int i=0; i<2; i++) { 173 int tile8[2][2];
142 for (int j=0; j<2; j++) { 174 bool sflipx[2][2];
143 sflipx[i][j] = (tile8[i][j] & 0x40) != 0; 175 bool sflipy[2][2];
144 sflipy[i][j] = (tile8[i][j] & 0x80) != 0;
145 176
146 tile8[i][j] &= 0x3f; 177 unsigned int magic = BufferView(mapTiles_).ReadFourBytes(tile16 * 8);
147 tile8[i][j] |= (ch & 0x3c0); 178
179 tile8[0][0] = mapTiles_[(tile16 * 8) + 4];
180 tile8[0][1] = mapTiles_[(tile16 * 8) + 5];
181 tile8[1][0] = mapTiles_[(tile16 * 8) + 6];
182 tile8[1][1] = mapTiles_[(tile16 * 8) + 7];
183
184 for (int i=0; i<2; i++) {
185 for (int j=0; j<2; j++) {
186 sflipx[i][j] = (tile8[i][j] & 0x40) != 0;
187 sflipy[i][j] = (tile8[i][j] & 0x80) != 0;
188
189 tile8[i][j] &= 0x3f;
190 tile8[i][j] |= (ch & 0x3c0);
191 }
148 } 192 }
149 }
150 193
151 unsigned int mask = (magic >> 16) & 0xf; 194 unsigned int mask = (magic >> 16) & 0xf;
152 if ((mask & 0x1) == 0) tile8[0][0] = -1; 195 if ((mask & 0x1) == 0) tile8[0][0] = -1;
153 if ((mask & 0x2) == 0) tile8[0][1] = -1; 196 if ((mask & 0x2) == 0) tile8[0][1] = -1;
154 if ((mask & 0x4) == 0) tile8[1][0] = -1; 197 if ((mask & 0x4) == 0) tile8[1][0] = -1;
155 if ((mask & 0x8) == 0) tile8[1][1] = -1; 198 if ((mask & 0x8) == 0) tile8[1][1] = -1;
156 199
157 for (int tiley=0; tiley<2; tiley++) { 200 for (int tiley=0; tiley<2; tiley++) {
158 for (int tilex=0; tilex<2; tilex++) { 201 for (int tilex=0; tilex<2; tilex++) {
159 if (tile8[tiley][tilex] < 0) continue; 202 if (tile8[tiley][tilex] < 0) continue;
160 203
161 int tileset = tile8[tiley][tilex] >> 6; 204 int tileset = tile8[tiley][tilex] >> 6;
162 int subtile = tile8[tiley][tilex] & 0x3f; 205 int subtile = tile8[tiley][tilex] & 0x3f;
163 206
164 int tileData[8][8]; 207 int tileData[8][8];
165 BufferView tilesetData(tilesets[tileset]); 208 BufferView tilesetData(tilesets_[tileset]);
166 tilesetData.Seek(subtile << 5); 209 tilesetData.Seek(subtile << 5);
167 for (int ty=0; ty<8; ty++) { 210 for (int ty=0; ty<8; ty++) {
168 for (int tx=0; tx<4; tx++) { 211 for (int tx=0; tx<4; tx++) {
169 unsigned char vvvv = tilesetData.ReadNextByte(); 212 unsigned char vvvv = tilesetData.ReadNextByte();
170 tileData[tx*2][ty] = static_cast<unsigned char>(vvvv & 0xF); 213 tileData[tx*2][ty] = static_cast<unsigned char>(vvvv & 0xF);
171 tileData[tx*2+1][ty] = static_cast<unsigned char>((vvvv >> 4) & 0xF); 214 tileData[tx*2+1][ty] = static_cast<unsigned char>((vvvv >> 4) & 0xF);
215 }
172 } 216 }
173 }
174 217
175 int stx = tflipx ? 1 - tilex : tilex; 218 int stx = tflipx ? 1 - tilex : tilex;
176 int sty = tflipy ? 1 - tiley : tiley; 219 int sty = tflipy ? 1 - tiley : tiley;
177 220
178 int destX = /*(mapx << 4) +*/ (stx << 3); 221 int destX = /*(mapx << 4) +*/ (stx << 3);
179 int destY = /*(mapy << 4) +*/ (sty << 3); 222 int destY = /*(mapy << 4) +*/ (sty << 3);
180 223
181 bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx); 224 bool reallyFlipX = (sflipx[tiley][tilex] ^ tflipx);
182 bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy); 225 bool reallyFlipY = (sflipy[tiley][tilex] ^ tflipy);
183 226
184 Magick::PixelPacket* pixels = view.get(destX,destY,8,8); 227 Magick::PixelPacket* pixels = view.get(destX,destY,8,8);
185 228
186 for (int ty=0; ty<8; ty++) { 229 for (int ty=0; ty<8; ty++) {
187 int actualTy = reallyFlipY ? (7-ty) : ty; 230 int actualTy = reallyFlipY ? (7-ty) : ty;
188 231
189 for (int tx=0; tx<8; tx++) { 232 for (int tx=0; tx<8; tx++) {
190 int actualTx = reallyFlipX ? (7-tx) : tx; 233 int actualTx = reallyFlipX ? (7-tx) : tx;
191 234
192 if (tileData[actualTx][actualTy] != 0) { 235 if (tileData[actualTx][actualTy] != 0) {
193 //auto& c = palette.Colors().at(tileData[actualTx][actualTy]); 236 //auto& c = palette.Colors().at(tileData[actualTx][actualTy]);
194 //std::cout << c.redQuantum() << "," << c.greenQuantum() << "," << c.blueQuantum() << std::endl; 237 //std::cout << c.redQuantum() << "," << c.greenQuantum() << "," << c.blueQuantum() << std::endl;
195 *pixels = palette.Colors().at(tileData[actualTx][actualTy]); 238 *pixels = palette.Colors().at(tileData[actualTx][actualTy]);
196 //std::cout << tileData[actualTx][actualTy] << std::endl; 239 //std::cout << tileData[actualTx][actualTy] << std::endl;
240 }
241 pixels++;
197 } 242 }
198 pixels++;
199 } 243 }
200 }
201 244
202 view.sync(); 245 view.sync();
246 }
203 } 247 }
248
249 return result;
204 } 250 }
205 251
206 return result; 252 const std::vector<std::vector<TileUse>>& getItemisedLayers() const {
207} 253 return itemised_;
254 }
208 255
209unsigned short stripFlipInfo(unsigned short ch) { 256 int getWidth() const { return width_; }
210 return ch & ~(0x4000 | 0x8000); 257
211} 258 int getHeight() const { return height_; }
259
260 int getRoomNum() const { return roomNum_; }
261
262private:
263
264 int roomNum_;
265 int width_;
266 int height_;
267 std::vector<Palette> palettes_;
268 std::vector<std::vector<char>> tilesets_;
269 std::vector<char> mapTiles_;
270 std::vector<std::vector<char>> mapLayers_;
271
272 identifier<unsigned short> metatiles_;
273 std::vector<std::vector<TileUse>> itemised_;
274};
275
276struct GlobalTile {
277 Magick::Image image;
278 std::string base64;
279
280 GlobalTile(Magick::Image input) : image(input) {
281 Magick::Blob blob;
282 input.write(&blob);
283 base64 = blob.base64();
284 }
285};
286
287class GlobalTileKeyExtract {
288public:
289
290 const std::string& operator()(const GlobalTile& globaltile) const {
291 return globaltile.base64;
292 }
293};
294
295using globaltile_identifier = identifier<GlobalTile, GlobalTileKeyExtract>;
296using globaltile_id = globaltile_identifier::id_type;
212 297
213int main(int argc, char** argv) { 298int main(int argc, char** argv) {
214 if (argc < 3) { 299 if (argc < 3) {
@@ -222,125 +307,102 @@ int main(int argc, char** argv) {
222 auto roomInfos = RoomInfo::ReadFromRom(m3.buffer()); 307 auto roomInfos = RoomInfo::ReadFromRom(m3.buffer());
223 auto roomGfxPals = RoomGfxPal::ReadFromRom(m3.buffer()); 308 auto roomGfxPals = RoomGfxPal::ReadFromRom(m3.buffer());
224 309
225 int roomNum = std::stoi(argv[2]); 310 std::list<Map> maps;
226 311 for (int i=2; i<argc; i++) {
227 std::cout << roomInfos[roomNum].width << "," << roomInfos[roomNum].height << std::endl; 312 int roomNum = std::stoi(argv[i]);
228 std::cout << roomGfxPals[roomNum].paletteId; 313 maps.emplace_back(m3.buffer(), roomNum, roomInfos[roomNum], roomGfxPals[roomNum]);
229 for (int i=0;i<12;i++) std::cout << "," << roomGfxPals[roomNum].tilesetId[i]; 314 }
230 std::cout << std::endl;
231 315
232 int width = roomInfos[roomNum].width; 316 globaltile_identifier globaltiles;
233 int height = roomInfos[roomNum].height; 317 for (const Map& map : maps) {
318 std::map<int, int> translatedTileIds;
234 319
235 Magick::Image image(Magick::Geometry(width*16, height*16), "transparent"); 320 // Generate map datafile.
321 std::ofstream mapfile("out" + std::to_string(map.getRoomNum()) + ".tmx");
322 mapfile << R"(<map version="1.0" orientation="orthogonal" renderorder="right-down" width=")";
323 mapfile << map.getWidth();
324 mapfile << R"(" height=")";
325 mapfile << map.getHeight();
326 mapfile << R"(" tilewidth="16" tileheight="16">)" << std::endl;
327 mapfile << R"( <tileset firstgid="1" source="out.tsx" />)" << std::endl;
328
329 for (int layer = map.getItemisedLayers().size()-1; layer >= 0; layer--) {
330 mapfile << R"( <layer id=")";
331 mapfile << layer;
332 mapfile << R"(" name="Layer )";
333 mapfile << layer;
334 mapfile << R"(" width=")";
335 mapfile << map.getWidth();
336 mapfile << R"(" height=")";
337 mapfile << map.getHeight();
338 mapfile << R"(">)" << std::endl;
339 mapfile << R"( <data encoding="csv">)";
340
341 bool first = true;
342 for (const TileUse& tu : map.getItemisedLayers()[layer]) {
343 if (first) {
344 first = false;
345 } else {
346 mapfile << ",";
347 }
236 348
237 auto mapTiles = GetMapTiles(m3.buffer(), roomNum); 349 if (!translatedTileIds.count(tu.id)) {
238 auto mapLayers = GetMapLayers(m3.buffer(), roomNum); 350 Magick::Image renderedTile = map.renderTile(tu.id, false, false);
351 renderedTile.magick("png");
239 352
240 const auto& gfxPal = roomGfxPals[roomNum]; 353 GlobalTile gt(std::move(renderedTile));
241 auto palettes = gfxPal.GetPalettes(m3.buffer()); 354 translatedTileIds[tu.id] = globaltiles.add(gt);
242 auto tilesets = gfxPal.GetTilesets(m3.buffer()); 355 }
243 356
244 identifier<unsigned short> metatiles; 357 unsigned int outChar = translatedTileIds[tu.id] + 1;
245 std::vector<std::vector<TileUse>> itemised; 358 if (tu.tflipx) outChar |= 0x80000000;
359 if (tu.tflipy) outChar |= 0x40000000;
360 mapfile << outChar;
361 }
246 362
247 for (int layer=0; layer<mapLayers.size(); layer++) { 363 mapfile << R"(</data>)" << std::endl;
248 const std::vector<char>& ml = mapLayers[layer]; 364 mapfile << R"( </layer>)" << std::endl;
249 if (ml.empty()) continue; 365 }
250 366
251 std::vector<TileUse> newLayer; 367 mapfile << R"(</map>)" << std::endl;
252 368
253 for (int mapy = 0; mapy < height; mapy++) { 369 // Render map to image.
254 for (int mapx = 0; mapx < width; mapx++) { 370 /*for (int layer=itemised.size()-1; layer>=0; layer--) {
255 unsigned short ch = BufferView(ml).ReadTwoBytes((mapx + (mapy * width)) * 2); 371 for (int mapy = 0; mapy < height; mapy++) {
256 TileUse tu; 372 for (int mapx = 0; mapx < width; mapx++) {
257 tu.id = metatiles.add(stripFlipInfo(ch)); 373 const TileUse& tu = itemised[layer][mapx+mapy*width];
258 tu.tflipx = (ch & 0x4000) != 0; 374 Magick::Image tileRender = renderTile(metatiles.get(tu.id), tu.tflipx, tu.tflipy, palettes, mapTiles, tilesets);
259 tu.tflipy = (ch & 0x8000) != 0; 375 image.composite(tileRender, mapx << 4, mapy << 4, Magick::OverCompositeOp);
260 newLayer.push_back(std::move(tu)); 376 }
261 } 377 }
262 } 378 }*/
263
264 itemised.push_back(std::move(newLayer));
265 } 379 }
266 380
267 constexpr int TILES_PER_ROW = 10; 381 constexpr int TILES_PER_ROW = 10;
268 int sheetWidth; 382 int sheetWidth;
269 int sheetHeight; 383 int sheetHeight;
270 384
271 if (metatiles.size() < TILES_PER_ROW) { 385 if (globaltiles.size() < TILES_PER_ROW) {
272 sheetWidth = metatiles.size() * 16; 386 sheetWidth = globaltiles.size() * 16;
273 sheetHeight = 16; 387 sheetHeight = 16;
274 } else { 388 } else {
275 sheetWidth = TILES_PER_ROW * 16; 389 sheetWidth = TILES_PER_ROW * 16;
276 sheetHeight = (metatiles.size() / TILES_PER_ROW + 1) * 16; 390 sheetHeight = (globaltiles.size() / TILES_PER_ROW + 1) * 16;
277 }
278
279 // Generate map datafile.
280 std::ofstream mapfile("out.tmx");
281 mapfile << R"(<map version="1.0" orientation="orthogonal" renderorder="right-down" width=")";
282 mapfile << width;
283 mapfile << R"(" height=")";
284 mapfile << height;
285 mapfile << R"(" tilewidth="16" tileheight="16">)" << std::endl;
286 mapfile << R"( <tileset firstgid="1" name="fromRom" tilewidth="16" tileheight="16" tilecount=")";
287 mapfile << metatiles.size();
288 mapfile << R"(" columns=")";
289 mapfile << TILES_PER_ROW;
290 mapfile << R"(">)" << std::endl;
291 mapfile << R"( <image source="tiles.png" />)" << std::endl;
292 mapfile << R"( </tileset>)" << std::endl;
293
294 for (int layer=itemised.size()-1; layer>=0; layer--) {
295 mapfile << R"( <layer id=")";
296 mapfile << layer;
297 mapfile << R"(" name="Layer )";
298 mapfile << layer;
299 mapfile << R"(" width=")";
300 mapfile << width;
301 mapfile << R"(" height=")";
302 mapfile << height;
303 mapfile << R"(">)" << std::endl;
304 mapfile << R"( <data encoding="csv">)";
305
306 bool first = true;
307 for (const TileUse& tu : itemised[layer]) {
308 if (first) {
309 first = false;
310 } else {
311 mapfile << ",";
312 }
313
314 unsigned int outChar = tu.id + 1;
315 if (tu.tflipx) outChar |= 0x80000000;
316 if (tu.tflipy) outChar |= 0x40000000;
317 mapfile << outChar;
318 }
319
320 mapfile << R"(</data>)" << std::endl;
321 mapfile << R"( </layer>)" << std::endl;
322 }
323
324 mapfile << R"(</map>)" << std::endl;
325
326 // Render map to image.
327 for (int layer=itemised.size()-1; layer>=0; layer--) {
328 for (int mapy = 0; mapy < height; mapy++) {
329 for (int mapx = 0; mapx < width; mapx++) {
330 const TileUse& tu = itemised[layer][mapx+mapy*width];
331 Magick::Image tileRender = renderTile(metatiles.get(tu.id), tu.tflipx, tu.tflipy, palettes, mapTiles, tilesets);
332 image.composite(tileRender, mapx << 4, mapy << 4, Magick::OverCompositeOp);
333 }
334 }
335 } 391 }
336 392
337 image.magick("png"); 393 std::ofstream tilesetfile("out.tsx");
338 image.write("out.png"); 394 tilesetfile << R"(<tileset name="fromRom" tilewidth="16" tileheight="16" tilecount=")";
395 tilesetfile << globaltiles.size();
396 tilesetfile << R"(" columns=")";
397 tilesetfile << TILES_PER_ROW;
398 tilesetfile << R"(">)" << std::endl;
399 tilesetfile << R"( <image source="tiles.png" />)" << std::endl;
400 tilesetfile << R"(</tileset>)" << std::endl;
339 401
340 // Render tileset image. 402 // Render tileset image.
341 Magick::Image tilesetImage(Magick::Geometry(sheetWidth, sheetHeight), "transparent"); 403 Magick::Image tilesetImage(Magick::Geometry(sheetWidth, sheetHeight), "transparent");
342 for (int i=0; i<metatiles.size(); i++) { 404 for (int i=0; i<globaltiles.size(); i++) {
343 Magick::Image tileRender = renderTile(metatiles.get(i), false, false, palettes, mapTiles, tilesets); 405 const Magick::Image& tileRender = globaltiles.get(i).image;
344 tilesetImage.composite(tileRender, (i % TILES_PER_ROW) << 4, (i / TILES_PER_ROW) << 4, Magick::OverCompositeOp); 406 tilesetImage.composite(tileRender, (i % TILES_PER_ROW) << 4, (i / TILES_PER_ROW) << 4, Magick::OverCompositeOp);
345 } 407 }
346 408