diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/sprite_dumper/identifier.h | 26 | ||||
-rw-r--r-- | 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 @@ | |||
5 | #include <vector> | 5 | #include <vector> |
6 | 6 | ||
7 | template <typename T> | 7 | template <typename T> |
8 | class DefaultKeyExtract { | ||
9 | public: | ||
10 | |||
11 | const T& operator()(const T& val) const { | ||
12 | return val; | ||
13 | } | ||
14 | }; | ||
15 | |||
16 | template <typename T, typename KeyExtract = DefaultKeyExtract<T>> | ||
8 | class identifier { | 17 | class identifier { |
9 | public: | 18 | public: |
10 | 19 | ||
@@ -12,20 +21,21 @@ public: | |||
12 | 21 | ||
13 | private: | 22 | private: |
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 | ||
17 | public: | 27 | public: |
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 | ||
53 | private: | 63 | private: |
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 | ||
107 | using metatile_id = identifier<unsigned short>::key_type; | ||
108 | |||
109 | struct TileUse { | 107 | struct 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 | ||
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) { | 113 | unsigned 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; | 117 | class Map { |
119 | if ((tile16 >> 6) >= 12) return result; | 118 | public: |
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 | ||
209 | unsigned 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 | |||
262 | private: | ||
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 | |||
276 | struct 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 | |||
287 | class GlobalTileKeyExtract { | ||
288 | public: | ||
289 | |||
290 | const std::string& operator()(const GlobalTile& globaltile) const { | ||
291 | return globaltile.base64; | ||
292 | } | ||
293 | }; | ||
294 | |||
295 | using globaltile_identifier = identifier<GlobalTile, GlobalTileKeyExtract>; | ||
296 | using globaltile_id = globaltile_identifier::id_type; | ||
212 | 297 | ||
213 | int main(int argc, char** argv) { | 298 | int 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 | ||