]> git.lizzy.rs Git - dragonfireclient.git/blob - util/minetestmapper.py
extended content-type range
[dragonfireclient.git] / util / minetestmapper.py
1 #!/usr/bin/python2
2 # -*- coding: windows-1252 -*-
3
4 # This program is free software. It comes without any warranty, to
5 # the extent permitted by applicable law. You can redistribute it
6 # and/or modify it under the terms of the Do What The Fuck You Want
7 # To Public License, Version 2, as published by Sam Hocevar. See
8 # COPYING for more details.
9
10 # Made by Jogge, modified by celeron55
11 # 2011-05-29: j0gge: initial release
12 # 2011-05-30: celeron55: simultaneous support for sectors/sectors2, removed 
13 # 2011-06-02: j0gge: command line parameters, coordinates, players, ...
14 # 2011-06-04: celeron55: added #!/usr/bin/python2 and converted \r\n to \n
15 #                        to make it easily executable on Linux
16
17 # Requires Python Imaging Library: http://www.pythonware.com/products/pil/
18
19 # Some speed-up: ...lol, actually it slows it down.
20 #import psyco ; psyco.full() 
21 #from psyco.classes import *
22
23 import zlib
24 import Image, ImageDraw, ImageFont, ImageColor
25 import os
26 import string
27 import time
28 import getopt
29 import sys
30
31 def hex_to_int(h):
32         i = int(h, 16)
33         if(i > 2047):
34                 i -= 4096
35         return i
36
37 def hex4_to_int(h):
38         i = int(h, 16)
39         if(i > 32767):
40                 i -= 65536
41         return i
42
43 def int_to_hex3(i):
44         if(i < 0):
45                 return "%03X" % (i + 4096)
46         else:
47                 return "%03X" % i
48
49 def int_to_hex4(i):
50         if(i < 0):
51                 return "%04X" % (i + 65536)
52         else:
53                 return "%04X" % i
54
55 def limit(i, l, h):
56         if(i > h):
57                 i = h
58         if(i < l):
59                 i = l
60         return i
61
62 def usage():
63         print "TODO: Help"
64 try:
65         opts, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input=", "output=", "bgcolor=", "scalecolor=", "origincolor=", "playercolor=", "draworigin", "drawplayers", "drawscale"])
66 except getopt.GetoptError, err:
67         # print help information and exit:
68         print str(err) # will print something like "option -a not recognized"
69         usage()
70         sys.exit(2)
71
72 path = "../world/"
73 output = "uloste.png"
74 border = 0
75 scalecolor = "black"
76 bgcolor = "white"
77 origincolor = "red"
78 playercolor = "red"
79 drawscale = False
80 drawplayers = False
81 draworigin = False
82
83 sector_xmin = -1500 / 16
84 sector_xmax = 1500 / 16
85 sector_zmin = -1500 / 16
86 sector_zmax = 1500 / 16
87
88 for o, a in opts:
89         if o in ("-h", "--help"):
90                 usage()
91                 sys.exit()
92         elif o in ("-i", "--input"):
93                 path = a
94         elif o in ("-o", "--output"):
95                 output = a
96         elif o == "--bgcolor":
97                 bgcolor = ImageColor.getrgb(a)
98         elif o == "--scalecolor":
99                 scalecolor = ImageColor.getrgb(a)
100         elif o == "--playercolor":
101                 playercolor = ImageColor.getrgb(a)
102         elif o == "--origincolor":
103                 origincolor = ImageColor.getrgb(a)
104         elif o == "--drawscale":
105                 drawscale = True
106                 border = 40
107         elif o == "--drawplayers":
108                 drawplayers = True
109         elif o == "--draworigin":
110                 draworigin = True
111         else:
112                 assert False, "unhandled option"
113         
114 if path[-1:]!="/" and path[-1:]!="\\":
115         path = path + "/"
116
117 # Load color information for the blocks.
118 colors = {}
119 f = file("colors.txt")
120 for line in f:
121         values = string.split(line)
122         colors[int(values[0])] = (int(values[1]), int(values[2]), int(values[3]))
123 f.close()
124
125 xlist = []
126 zlist = []
127
128 # List all sectors to memory and calculate the width and heigth of the resulting picture.
129 try:
130         for filename in os.listdir(path + "sectors2"):
131                 for filename2 in os.listdir(path + "sectors2/" + filename):
132                         x = hex_to_int(filename)
133                         z = hex_to_int(filename2)
134                         if x < sector_xmin or x > sector_xmax:
135                                 continue
136                         if z < sector_zmin or z > sector_zmax:
137                                 continue
138                         xlist.append(x)
139                         zlist.append(z)
140 except OSError:
141         pass
142 try:
143         for filename in os.listdir(path + "sectors"):
144                 x = hex4_to_int(filename[:4])
145                 z = hex4_to_int(filename[-4:])
146                 if x < sector_xmin or x > sector_xmax:
147                         continue
148                 if z < sector_zmin or z > sector_zmax:
149                         continue
150                 xlist.append(x)
151                 zlist.append(z)
152 except OSError:
153         pass
154
155 minx = min(xlist)
156 minz = min(zlist)
157 maxx = max(xlist)
158 maxz = max(zlist)
159
160 w = (maxx - minx) * 16 + 16
161 h = (maxz - minz) * 16 + 16
162
163 print "w="+str(w)+" h="+str(h)
164
165 im = Image.new("RGB", (w + border, h + border), bgcolor)
166 draw = ImageDraw.Draw(im)
167 impix = im.load()
168
169 stuff = {}
170
171 starttime = time.time()
172
173 def data_is_air(d):
174         return (d == 254 or d == 126)
175
176 # Go through all sectors.
177 for n in range(len(xlist)):
178         #if n > 500:
179         #       break
180         if n % 200 == 0:
181                 nowtime = time.time()
182                 dtime = nowtime - starttime
183                 try:
184                         n_per_second = 1.0 * n / dtime
185                 except ZeroDivisionError:
186                         n_per_second = 0
187                 if n_per_second != 0:
188                         seconds_per_n = 1.0 / n_per_second
189                         time_guess = seconds_per_n * len(xlist)
190                         remaining_s = time_guess - dtime
191                         remaining_minutes = int(remaining_s / 60)
192                         remaining_s -= remaining_minutes * 60;
193                         print("Processing sector "+str(n)+" of "+str(len(xlist))
194                                         +" ("+str(round(100.0*n/len(xlist), 1))+"%)"
195                                         +" (ETA: "+str(remaining_minutes)+"m "
196                                         +str(int(remaining_s))+"s)")
197
198         xpos = xlist[n]
199         zpos = zlist[n]
200         
201         xhex = int_to_hex3(xpos)
202         zhex = int_to_hex3(zpos)
203         xhex4 = int_to_hex4(xpos)
204         zhex4 = int_to_hex4(zpos)
205         
206         sector1 = xhex4.lower() + zhex4.lower()
207         sector2 = xhex.lower() + "/" + zhex.lower()
208         
209         ylist = []
210         
211         sectortype = ""
212         
213         try:
214                 for filename in os.listdir(path + "sectors/" + sector1):
215                         if(filename != "meta"):
216                                 pos = int(filename, 16)
217                                 if(pos > 32767):
218                                         pos -= 65536
219                                 ylist.append(pos)
220                                 sectortype = "old"
221         except OSError:
222                 pass
223         
224         if sectortype != "old":
225                 try:
226                         for filename in os.listdir(path + "sectors2/" + sector2):
227                                 if(filename != "meta"):
228                                         pos = int(filename, 16)
229                                         if(pos > 32767):
230                                                 pos -= 65536
231                                         ylist.append(pos)
232                                         sectortype = "new"
233                 except OSError:
234                         pass
235         
236         if sectortype == "":
237                 continue
238
239         ylist.sort()
240         
241         # Make a list of pixels of the sector that are to be looked for.
242         pixellist = []
243         water = {}
244         for x in range(16):
245                 for z in range(16):
246                         pixellist.append((x, z))
247                         water[(x, z)] = 0
248         
249         # Go through the Y axis from top to bottom.
250         ylist2=[]
251         for ypos in reversed(ylist):
252                 
253                 yhex = int_to_hex4(ypos)
254
255                 filename = ""
256                 if sectortype == "old":
257                         filename = path + "sectors/" + sector1 + "/" + yhex.lower()
258                 else:
259                         filename = path + "sectors2/" + sector2 + "/" + yhex.lower()
260
261                 f = file(filename, "rb")
262
263                 version = f.read(1)
264                 flags = f.read(1)
265                 
266                 # Checking day and night differs -flag
267                 if not ord(flags) & 2:
268                         ylist2.append((ypos,filename))
269                         f.close()
270                         continue
271
272                 dec_o = zlib.decompressobj()
273                 try:
274                         mapdata = dec_o.decompress(f.read())
275                 except:
276                         mapdata = []
277                         
278                 f.close()
279                 
280                 if(len(mapdata) < 4096):
281                         print "bad: " + xhex + "/" + zhex + "/" + yhex + " " + str(len(mapdata))
282                 else:
283                         chunkxpos = xpos * 16
284                         chunkypos = ypos * 16
285                         chunkzpos = zpos * 16
286                         for (x, z) in reversed(pixellist):
287                                 for y in reversed(range(16)):
288                                         datapos = x + y * 16 + z * 256
289                                         if(not data_is_air(ord(mapdata[datapos])) and ord(mapdata[datapos]) in colors):
290                                                 if(ord(mapdata[datapos]) == 2 or ord(mapdata[datapos]) == 9):
291                                                         water[(x, z)] += 1
292                                                         # Add dummy stuff for drawing sea without seabed
293                                                         stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)])
294                                                 else:
295                                                         pixellist.remove((x, z))
296                                                         # Memorize information on the type and height of the block and for drawing the picture.
297                                                         stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)])
298                                                         break
299                                         elif(not data_is_air(ord(mapdata[datapos])) and ord(mapdata[datapos]) not in colors):
300                                                 print "strange block: " + xhex + "/" + zhex + "/" + yhex + " x: " + str(x) + " y: " + str(y) + " z: " + str(z) + " palikka: " + str(ord(mapdata[datapos]))
301                 
302                 # After finding all the pixels in the sector, we can move on to the next sector without having to continue the Y axis.
303                 if(len(pixellist) == 0):
304                         break
305         
306         if len(pixellist) > 0:
307                 for (ypos, filename) in ylist2:
308                         f = file(filename, "rb")
309
310                         version = f.read(1)
311                         flags = f.read(1)
312
313                         dec_o = zlib.decompressobj()
314                         try:
315                                 mapdata = dec_o.decompress(f.read())
316                         except:
317                                 mapdata = []
318                                 
319                         f.close()
320                         
321                         if(len(mapdata) < 4096):
322                                 print "bad: " + xhex + "/" + zhex + "/" + yhex + " " + str(len(mapdata))
323                         else:
324                                 chunkxpos = xpos * 16
325                                 chunkypos = ypos * 16
326                                 chunkzpos = zpos * 16
327                                 for (x, z) in reversed(pixellist):
328                                         for y in reversed(range(16)):
329                                                 datapos = x + y * 16 + z * 256
330                                                 if(not data_is_air(ord(mapdata[datapos])) and ord(mapdata[datapos]) in colors):
331                                                         if(ord(mapdata[datapos]) == 2 or ord(mapdata[datapos]) == 9):
332                                                                 water[(x, z)] += 1
333                                                                 # Add dummy stuff for drawing sea without seabed
334                                                                 stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)])
335                                                         else:
336                                                                 pixellist.remove((x, z))
337                                                                 # Memorize information on the type and height of the block and for drawing the picture.
338                                                                 stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, ord(mapdata[datapos]), water[(x, z)])
339                                                                 break
340                                                 elif(not data_is_air(ord(mapdata[datapos])) and ord(mapdata[datapos]) not in colors):
341                                                         print "outo palikka: " + xhex + "/" + zhex + "/" + yhex + " x: " + str(x) + " y: " + str(y) + " z: " + str(z) + " palikka: " + str(ord(mapdata[datapos]))
342                         
343                         # After finding all the pixels in the sector, we can move on to the next sector without having to continue the Y axis.
344                         if(len(pixellist) == 0):
345                                 break
346
347 print "Drawing image"
348 # Drawing the picture
349 starttime = time.time()
350 n = 0
351 for (x, z) in stuff.iterkeys():
352         if n % 500000 == 0:
353                 nowtime = time.time()
354                 dtime = nowtime - starttime
355                 try:
356                         n_per_second = 1.0 * n / dtime
357                 except ZeroDivisionError:
358                         n_per_second = 0
359                 if n_per_second != 0:
360                         listlen = len(stuff)
361                         seconds_per_n = 1.0 / n_per_second
362                         time_guess = seconds_per_n * listlen
363                         remaining_s = time_guess - dtime
364                         remaining_minutes = int(remaining_s / 60)
365                         remaining_s -= remaining_minutes * 60;
366                         print("Drawing pixel "+str(n)+" of "+str(listlen)
367                                         +" ("+str(round(100.0*n/listlen, 1))+"%)"
368                                         +" (ETA: "+str(remaining_minutes)+"m "
369                                         +str(int(remaining_s))+"s)")
370         n += 1
371
372         (r, g, b) = colors[stuff[(x,z)][1]]
373         # Comparing heights of a couple of adjacent blocks and changing brightness accordingly.
374         try:
375                 c1 = stuff[(x - 1, z)][1]
376                 c2 = stuff[(x, z + 1)][1]
377                 c = stuff[(x, z)][1]
378                 if c1 != 2 and c1 != 9 and c2 != 2 and c2 != 9 and c != 2 and c != 9:
379                         y1 = stuff[(x - 1, z)][0]
380                         y2 = stuff[(x, z + 1)][0]
381                         y = stuff[(x, z)][0]
382                         
383                         d = ((y - y1) + (y - y2)) * 12
384                 else:
385                         d = 0
386                 
387                 if(d > 36):
388                         d = 36
389                         
390                 r = limit(r + d, 0, 255)
391                 g = limit(g + d, 0, 255)
392                 b = limit(b + d, 0, 255)
393         except:
394                 pass
395         
396         # Water
397         if(stuff[(x,z)][2] > 0):
398                 r=int(r * .15 + colors[2][0] * .85)
399                 g=int(g * .15 + colors[2][1] * .85)
400                 b=int(b * .15 + colors[2][2] * .85)
401                 
402         impix[x - minx * 16 + border, h - 1 - (z - minz * 16) + border] = (r, g, b)
403
404
405 if draworigin:
406         draw.ellipse((minx * -16 - 5 + border, h - minz * -16 - 6 + border, minx * -16 + 5 + border, h - minz * -16 + 4 + border), outline = origincolor)
407
408 font = ImageFont.load_default()
409
410 if drawscale:
411         draw.text((24, 0), "X", font = font, fill = scalecolor)
412         draw.text((2, 24), "Z", font = font, fill = scalecolor)
413
414         for n in range(int(minx / -4) * -4, maxx, 4):
415                 draw.text((minx * -16 + n * 16 + 2 + border, 0), str(n * 16), font = font, fill = scalecolor)
416                 draw.line((minx * -16 + n * 16 + border, 0, minx * -16 + n * 16 + border, border - 1), fill = scalecolor)
417
418         for n in range(int(maxz / 4) * 4, minz, -4):
419                 draw.text((2, h - 1 - (n * 16 - minz * 16) + border), str(n * 16), font = font, fill = scalecolor)
420                 draw.line((0, h - 1 - (n * 16 - minz * 16) + border, border - 1, h - 1 - (n * 16 - minz * 16) + border), fill = scalecolor)
421
422 if drawplayers:
423         try:
424                 for filename in os.listdir(path + "players"):
425                         f = file(path + "players/" + filename)
426                         lines = f.readlines()
427                         name=""
428                         position=[]
429                         for line in lines:
430                                 p = string.split(line)
431                                 if p[0] == "name":
432                                         name = p[2]
433                                         print filename + ": name = " + name
434                                 if p[0] == "position":
435                                         position = string.split(p[2][1:-1], ",")
436                                         print filename + ": position = " + p[2]
437                         if len(name) > 0 and len(position) == 3:
438                                 x=(int(float(position[0]) / 10 - minx * 16))
439                                 z=int(h - (float(position[2]) / 10 - minz * 16))
440                                 draw.ellipse((x - 2 + border, z - 2 + border, x + 2 + border, z + 2 + border), outline = playercolor)
441                                 draw.text((x + 2 + border, z + 2 + border), name, font = font, fill = playercolor)
442                         f.close()
443         except OSError:
444                 pass
445
446 print "Saving"
447 im.save(output)