diff --git a/minetestmapper-expertmm.py b/minetestmapper-expertmm.py index e233478..7a27af3 100644 --- a/minetestmapper-expertmm.py +++ b/minetestmapper-expertmm.py @@ -1,13 +1,7 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -# This program is free software. It comes without any warranty, to -# the extent permitted by applicable law. You can redistribute it -# and/or modify it under the terms of the WTFPL -# Public License, Version 2, as published by Sam Hocevar. See -# COPYING for more details. - -# Made by Jogge, modified by celeron55 +# Made by Jogge, modified by: celeron55, expertmm # 2011-05-29: j0gge: initial release # 2011-05-30: celeron55: simultaneous support for sectors/sectors2, removed # 2011-06-02: j0gge: command line parameters, coordinates, players, ... @@ -16,12 +10,15 @@ # 2011-07-30: WF: Support for content types extension, refactoring # 2011-07-30: erlehmann: PEP 8 compliance. # 2016-03-08: expertmm: geometry and region params +# 2017-03-17: expertmm: removed license from this file (this file should fall +# under the license of minetest) +# 2017-04-12: expertmm: PEP8 compliance # Requires Python Imaging Library: http://www.pythonware.com/products/pil/ # Some speed-up: ...lol, actually it slows it down. -#import psyco ; psyco.full() -#from psyco.classes import * +# import psyco ; psyco.full() +# from psyco.classes import * import zlib import os @@ -89,19 +86,22 @@ def int_to_hex4(i): def getBlockAsInteger(p): return p[2]*16777216 + p[1]*4096 + p[0] + def unsignedToSigned(i, max_positive): if i < max_positive: return i else: return i - 2*max_positive + def getIntegerAsBlock(i): x = unsignedToSigned(i % 4096, 2048) i = int((i - x) / 4096) y = unsignedToSigned(i % 4096, 2048) i = int((i - y) / 4096) z = unsignedToSigned(i % 4096, 2048) - return x,y,z + return x, y, z + def limit(i, l, h): if(i > h): @@ -110,17 +110,24 @@ def limit(i, l, h): i = l return i + def readU8(f): return ord(f.read(1)) + def readU16(f): return ord(f.read(1))*256 + ord(f.read(1)) + def readU32(f): - return ord(f.read(1))*256*256*256 + ord(f.read(1))*256*256 + ord(f.read(1))*256 + ord(f.read(1)) + return ord(f.read(1))*256*256*256 + ord(f.read(1))*256*256 + \ + ord(f.read(1))*256 + ord(f.read(1)) + def readS32(f): - return unsignedToSigned(ord(f.read(1))*256*256*256 + ord(f.read(1))*256*256 + ord(f.read(1))*256 + ord(f.read(1)), 2**31) + return unsignedToSigned(ord(f.read(1))*256*256*256 + + ord(f.read(1))*256*256 + ord(f.read(1))*256 + + ord(f.read(1)), 2**31) usagetext = """minetestmapper.py [options] -i/--input @@ -226,24 +233,24 @@ if geometry_string is not None: nonchunky_xmax = nonchunky_xmin + this_width - 1 # inclusive rect nonchunky_zmin = z nonchunky_zmax = nonchunky_zmin + this_height - 1 # inclusive rect - print("#geometry:") - print("# x:" + str(x)) - print("# z:" + str(z)) - print("# width:" + str(this_width)) - print("# height:" + str(this_height)) - print("region:") - print(" xmin:" + str(nonchunky_xmin)) - print(" xmax:" + str(nonchunky_xmax)) - print(" zmin:" + str(nonchunky_zmin)) - print(" zmax:" + str(nonchunky_zmax)) + print("#geometry:" + "\n" + + "# x:" + str(x) + "\n" + + "# z:" + str(z) + "\n" + + "# width:" + str(this_width) + "\n" + + "# height:" + str(this_height) + "\n" + + "region:" + "\n" + + " xmin:" + str(nonchunky_xmin) + "\n" + + " xmax:" + str(nonchunky_xmax) + "\n" + + " zmin:" + str(nonchunky_zmin) + "\n" + + " zmax:" + str(nonchunky_zmax)) else: print("ERROR: Missing coordinates in '" + geometry_string + "' for geometry (must be in the form: x:z+width+height)") usage() sys.exit(2) else: - print("ERROR: Incorrect value '" + geometry_string + - "' for geometry (must be in the form: x:z+width+height)") + print("ERROR: Incorrect geometry syntax '" + geometry_string + + "' (must be in the form: x:z+width+height)") usage() sys.exit(2) elif region_string is not None: @@ -258,11 +265,11 @@ elif region_string is not None: nonchunky_xmax = int(xmax_string) nonchunky_zmin = int(zmin_string) nonchunky_zmax = int(zmax_string) - print("region:") - print(" xmin:" + str(nonchunky_xmin)) - print(" xmax:" + str(nonchunky_xmax)) - print(" zmin:" + str(nonchunky_zmin)) - print(" zmax:" + str(nonchunky_zmax)) + print("region:" + "\n" + + " xmin:" + str(nonchunky_xmin) + "\n" + + " xmax:" + str(nonchunky_xmax) + "\n" + + " zmin:" + str(nonchunky_zmin) + "\n" + + " zmax:" + str(nonchunky_zmax)) else: print("ERROR: Incorrect value '" + region_string + "' for region (must be in the form: xmin:xmax,zmin:zmax)") @@ -341,8 +348,8 @@ for line in f: int(values[3])) f.close() -#print("colors: "+repr(colors)) -#sys.exit(1) +# print("colors: "+repr(colors)) +# sys.exit(1) xlist = [] zlist = [] @@ -411,8 +418,8 @@ maxz = max(zlist) w = (maxx - minx) * 16 + 16 h = (maxz - minz) * 16 + 16 -print("Result image (w=" + str(w) + " h=" + str(h) + ") will be written to " - + output) +print("Result image (w=" + str(w) + " h=" + str(h) + ") will be written to " + + output) im = Image.new("RGB", (w + border, h + border), bgcolor) draw = ImageDraw.Draw(im) @@ -427,15 +434,19 @@ starttime = time.time() CONTENT_WATER = 2 + def content_is_ignore(d): return d in [0, "ignore"] + def content_is_water(d): return d in [2, 9] + def content_is_air(d): return d in [126, 127, 254, "air"] + def read_content(mapdata, version, datapos): if version >= 24: return (mapdata[datapos*2] << 8) | (mapdata[datapos*2 + 1]) @@ -450,14 +461,15 @@ def read_content(mapdata, version, datapos): raise Exception("Unsupported map format: " + str(version)) -def read_mapdata(mapdata, version, pixellist, water, day_night_differs, id_to_name): +def read_mapdata(mapdata, version, pixellist, water, day_night_differs, + id_to_name): global stuff # oh my :-) global unknown_node_names global unknown_node_ids if(len(mapdata) < 4096): - print("bad: " + xhex + "/" + zhex + "/" + yhex + " " + \ - str(len(mapdata))) + print("bad: " + xhex + "/" + zhex + "/" + yhex + " " + + str(len(mapdata))) else: chunkxpos = xpos * 16 chunkypos = ypos * 16 @@ -481,31 +493,37 @@ def read_mapdata(mapdata, version, pixellist, water, day_night_differs, id_to_na elif content_is_water(content): water[(x, z)] += 1 # Add dummy stuff for drawing sea without seabed - stuff[(chunkxpos + x, chunkzpos + z)] = ( - chunkypos + y, content, water[(x, z)], day_night_differs) + stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, + content, + water[(x, z)], + day_night_differs) elif content in colors: # Memorize information on the type and height of # the block and for drawing the picture. - stuff[(chunkxpos + x, chunkzpos + z)] = ( - chunkypos + y, content, water[(x, z)], day_night_differs) + stuff[(chunkxpos + x, chunkzpos + z)] = (chunkypos + y, + content, + water[(x, z)], + day_night_differs) pixellist.remove((x, z)) break else: if type(content) == str: if content not in unknown_node_names: unknown_node_names.append(content) - #print("unknown node: %s/%s/%s x: %d y: %d z: %d block name: %s" - # % (xhex, zhex, yhex, x, y, z, content)) + # print("unknown node: %s/%s/%s x: %d y: %d z: %d" + + # " block name: %s" + # % (xhex, zhex, yhex, x, y, z, content)) else: if content not in unknown_node_ids: unknown_node_ids.append(content) - #print("unknown node: %s/%s/%s x: %d y: %d z: %d block id: %x" - # % (xhex, zhex, yhex, x, y, z, content)) + # print("unknown node: %s/%s/%s x: %d y: %d z: %d" + + # " block id: %x" + # % (xhex, zhex, yhex, x, y, z, content)) # Go through all sectors. for n in range(len(xlist)): - #if n > 500: + # if n > 500: # break if n % 200 == 0: nowtime = time.time() @@ -520,10 +538,10 @@ for n in range(len(xlist)): remaining_s = time_guess - dtime remaining_minutes = int(remaining_s / 60) remaining_s -= remaining_minutes * 60 - print("Processing sector " + str(n) + " of " + str(len(xlist)) - + " (" + str(round(100.0 * n / len(xlist), 1)) + "%)" - + " (ETA: " + str(remaining_minutes) + "m " - + str(int(remaining_s)) + "s)") + print("Processing sector " + str(n) + " of " + str(len(xlist)) + + " (" + str(round(100.0 * n / len(xlist), 1)) + "%)" + + " (ETA: " + str(remaining_minutes) + "m " + + str(int(remaining_s)) + "s)") xpos = xlist[n] zpos = zlist[n] @@ -543,7 +561,9 @@ for n in range(len(xlist)): if cur: psmin = getBlockAsInteger((xpos, -2048, zpos)) psmax = getBlockAsInteger((xpos, 2047, zpos)) - cur.execute("SELECT `pos` FROM `blocks` WHERE `pos`>=? AND `pos`<=? AND (`pos` - ?) % 4096 = 0", (psmin, psmax, psmin)) + cur.execute("SELECT `pos` FROM `blocks` WHERE `pos`>=?" + " AND `pos`<=? AND (`pos` - ?) % 4096 = 0", + (psmin, psmax, psmin)) while True: r = cur.fetchone() if not r: @@ -590,13 +610,14 @@ for n in range(len(xlist)): # Go through the Y axis from top to bottom. for ypos in reversed(ylist): try: - #print("("+str(xpos)+","+str(ypos)+","+str(zpos)+")") + # print("("+str(xpos)+","+str(ypos)+","+str(zpos)+")") yhex = int_to_hex4(ypos) if sectortype == "sqlite": ps = getBlockAsInteger((xpos, ypos, zpos)) - cur.execute("SELECT `data` FROM `blocks` WHERE `pos`==? LIMIT 1", (ps,)) + cur.execute("SELECT `data` FROM `blocks`" + " WHERE `pos`==? LIMIT 1", (ps,)) r = cur.fetchone() if not r: continue @@ -605,15 +626,16 @@ for n in range(len(xlist)): if sectortype == "old": filename = path + "sectors/" + sector1 + "/" + yhex.lower() else: - filename = path + "sectors2/" + sector2 + "/" + yhex.lower() + filename = path + "sectors2/" + sector2 + "/" + \ + yhex.lower() f = file(filename, "rb") # Let's just memorize these even though it's not really necessary. version = readU8(f) flags = f.read(1) - #print("version="+str(version)) - #print("flags="+str(version)) + # print("version="+str(version)) + # print("flags="+str(version)) # Check flags is_underground = ((ord(flags) & 1) != 0) @@ -621,10 +643,10 @@ for n in range(len(xlist)): lighting_expired = ((ord(flags) & 4) != 0) generated = ((ord(flags) & 8) != 0) - #print("is_underground="+str(is_underground)) - #print("day_night_differs="+str(day_night_differs)) - #print("lighting_expired="+str(lighting_expired)) - #print("generated="+str(generated)) + # print("is_underground="+str(is_underground)) + # print("day_night_differs="+str(day_night_differs)) + # print("lighting_expired="+str(lighting_expired)) + # print("generated="+str(generated)) if version >= 22: content_width = readU8(f) @@ -638,9 +660,9 @@ for n in range(len(xlist)): mapdata = [] # Reuse the unused tail of the file - f.close(); + f.close() f = cStringIO.StringIO(dec_o.unused_data) - #print("unused data: "+repr(dec_o.unused_data)) + # print("unused data: "+repr(dec_o.unused_data)) # zlib-compressed node metadata list dec_o = zlib.decompressobj() @@ -651,9 +673,9 @@ for n in range(len(xlist)): metaliststr = [] # Reuse the unused tail of the file - f.close(); + f.close() f = cStringIO.StringIO(dec_o.unused_data) - #print("* dec_o.unused_data: "+repr(dec_o.unused_data)) + # print("* dec_o.unused_data: "+repr(dec_o.unused_data)) data_after_node_metadata = dec_o.unused_data if version <= 21: @@ -661,12 +683,12 @@ for n in range(len(xlist)): readU16(f) if version == 23: - readU8(f) # Unused node timer version (always 0) + readU8(f) # Unused node timer version (always 0) if version == 24: ver = readU8(f) if ver == 1: num = readU16(f) - for i in range(0,num): + for i in range(0, num): readU16(f) readS32(f) readS32(f) @@ -688,44 +710,46 @@ for n in range(len(xlist)): data = f.read(data_size) timestamp = readU32(f) - #print("* timestamp="+str(timestamp)) + # print("* timestamp="+str(timestamp)) id_to_name = {} if version >= 22: name_id_mapping_version = readU8(f) num_name_id_mappings = readU16(f) - #print("* num_name_id_mappings: "+str(num_name_id_mappings)) + # print("* num_name_id_mappings: "+str(num_name_id_mappings)) for i in range(0, num_name_id_mappings): node_id = readU16(f) name_len = readU16(f) name = f.read(name_len) - #print(str(node_id)+" = "+name) + # print(str(node_id)+" = "+name) id_to_name[node_id] = name # Node timers if version >= 25: timer_size = readU8(f) num = readU16(f) - for i in range(0,num): + for i in range(0, num): readU16(f) readS32(f) readS32(f) - read_mapdata(mapdata, version, pixellist, water, day_night_differs, id_to_name) + read_mapdata(mapdata, version, pixellist, water, day_night_differs, + id_to_name) # After finding all the pixels in the sector, we can move on to # the next sector without having to continue the Y axis. if(len(pixellist) == 0): break except Exception as e: - print("Error at ("+str(xpos)+","+str(ypos)+","+str(zpos)+"): "+str(e)) + print("Error at (" + str(xpos) + "," + str(ypos) + "," + + str(zpos) + "): " + str(e)) sys.stdout.write("Block data: ") for c in r[0]: - sys.stdout.write("%2.2x "%ord(c)) + sys.stdout.write("%2.2x " % ord(c)) sys.stdout.write(os.linesep) sys.stdout.write("Data after node metadata: ") for c in data_after_node_metadata: - sys.stdout.write("%2.2x "%ord(c)) + sys.stdout.write("%2.2x " % ord(c)) sys.stdout.write(os.linesep) traceback.print_exc() @@ -748,10 +772,10 @@ for (x, z) in stuff.iterkeys(): remaining_s = time_guess - dtime remaining_minutes = int(remaining_s / 60) remaining_s -= remaining_minutes * 60 - print("Drawing pixel " + str(n) + " of " + str(listlen) - + " (" + str(round(100.0 * n / listlen, 1)) + "%)" - + " (ETA: " + str(remaining_minutes) + "m " - + str(int(remaining_s)) + "s)") + print("Drawing pixel " + str(n) + " of " + str(listlen) + + " (" + str(round(100.0 * n / listlen, 1)) + "%)" + + " (ETA: " + str(remaining_minutes) + "m " + + str(int(remaining_s)) + "s)") n += 1 (r, g, b) = colors[stuff[(x, z)][1]] @@ -774,7 +798,7 @@ for (x, z) in stuff.iterkeys(): if not dnd: d = -69 elif not content_is_water(c1) and not content_is_water(c2) and \ - not content_is_water(c): + not content_is_water(c): y = stuff[(x, z)][0] y1 = stuff[(x - 1, z)][0] if dnd1 else y y2 = stuff[(x, z + 1)][0] if dnd2 else y @@ -802,8 +826,8 @@ for (x, z) in stuff.iterkeys(): if draworigin: draw.ellipse((minx * -16 - 5 + border, h - minz * -16 - 6 + border, - minx * -16 + 5 + border, h - minz * -16 + 4 + border), - outline=origincolor) + minx * -16 + 5 + border, h - minz * -16 + 4 + border), + outline=origincolor) font = ImageFont.load_default() @@ -813,15 +837,15 @@ if drawscale: for n in range(int(minx / -4) * -4, maxx, 4): draw.text((minx * -16 + n * 16 + 2 + border, 0), str(n * 16), - font=font, fill=scalecolor) + font=font, fill=scalecolor) draw.line((minx * -16 + n * 16 + border, 0, - minx * -16 + n * 16 + border, border - 1), fill=scalecolor) + minx * -16 + n * 16 + border, border - 1), fill=scalecolor) for n in range(int(maxz / 4) * 4, minz, -4): draw.text((2, h - 1 - (n * 16 - minz * 16) + border), str(n * 16), - font=font, fill=scalecolor) + font=font, fill=scalecolor) draw.line((0, h - 1 - (n * 16 - minz * 16) + border, border - 1, - h - 1 - (n * 16 - minz * 16) + border), fill=scalecolor) + h - 1 - (n * 16 - minz * 16) + border), fill=scalecolor) if drawplayers: try: @@ -842,9 +866,10 @@ if drawplayers: x = (int(float(position[0]) / 10 - minx * 16)) z = int(h - (float(position[2]) / 10 - minz * 16)) draw.ellipse((x - 2 + border, z - 2 + border, - x + 2 + border, z + 2 + border), outline=playercolor) + x + 2 + border, z + 2 + border), + outline=playercolor) draw.text((x + 2 + border, z + 2 + border), name, - font=font, fill=playercolor) + font=font, fill=playercolor) f.close() except OSError: pass diff --git a/singleimage.py b/singleimage.py index dc54687..a816866 100644 --- a/singleimage.py +++ b/singleimage.py @@ -115,6 +115,8 @@ class ChunkymapOfflineRenderer: print("") print("Running") print(" "+cmd_string) + print(" mapper_path: " + self.minetestmapper_py_path) + print(" backend: " + self.backend_string) print(" # (this may take a while...)") if os.path.isfile(tmp_png_path): os.remove(tmp_png_path)