poikilos
9 years ago
committed by
Jacob Gustafson
4 changed files with 903 additions and 55 deletions
@ -0,0 +1,6 @@ |
|||||
|
REM C:\Python27\python minetestmapper.py --input "C:\games\Minetest\worlds\try7amber" --region 0 64 0 64 --output "try7amber.png" |
||||
|
C:\Python27\python minetestmapper.py --input "C:\games\Minetest\worlds\try7amber" --geometry 0:0+16+16 --output "chunk_x0z0.png" > chunk_x0z0-genresult.txt |
||||
|
REM C:\Python27\python minetestmapper.py --input "C:\games\Minetest\worlds\try7amber" --geometry -100:-100+16+16 --output "chunk_x-100z-100.png" > chunk_x-100z-100-genresult.txt |
||||
|
|
||||
|
|
||||
|
pause |
@ -0,0 +1,818 @@ |
|||||
|
#!/usr/bin/env python2 |
||||
|
# expertmm fork from 2016-02-01 git version |
||||
|
# -*- 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 DWFYWT |
||||
|
# Public License, Version 2, as published by Sam Hocevar. See |
||||
|
# COPYING for more details. |
||||
|
|
||||
|
# Made by Jogge, modified by celeron55 |
||||
|
# 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, ... |
||||
|
# 2011-06-04: celeron55: added #!/usr/bin/python2 and converted \r\n to \n |
||||
|
# to make it easily executable on Linux |
||||
|
# 2011-07-30: WF: Support for content types extension, refactoring |
||||
|
# 2011-07-30: erlehmann: PEP 8 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 zlib |
||||
|
import os |
||||
|
import string |
||||
|
import time |
||||
|
import getopt |
||||
|
import sys |
||||
|
import array |
||||
|
import cStringIO |
||||
|
import traceback |
||||
|
from PIL import Image, ImageDraw, ImageFont, ImageColor |
||||
|
|
||||
|
TRANSLATION_TABLE = { |
||||
|
1: 0x800, # CONTENT_GRASS |
||||
|
4: 0x801, # CONTENT_TREE |
||||
|
5: 0x802, # CONTENT_LEAVES |
||||
|
6: 0x803, # CONTENT_GRASS_FOOTSTEPS |
||||
|
7: 0x804, # CONTENT_MESE |
||||
|
8: 0x805, # CONTENT_MUD |
||||
|
10: 0x806, # CONTENT_CLOUD |
||||
|
11: 0x807, # CONTENT_COALSTONE |
||||
|
12: 0x808, # CONTENT_WOOD |
||||
|
13: 0x809, # CONTENT_SAND |
||||
|
18: 0x80a, # CONTENT_COBBLE |
||||
|
19: 0x80b, # CONTENT_STEEL |
||||
|
20: 0x80c, # CONTENT_GLASS |
||||
|
22: 0x80d, # CONTENT_MOSSYCOBBLE |
||||
|
23: 0x80e, # CONTENT_GRAVEL |
||||
|
24: 0x80f, # CONTENT_SANDSTONE |
||||
|
25: 0x810, # CONTENT_CACTUS |
||||
|
26: 0x811, # CONTENT_BRICK |
||||
|
27: 0x812, # CONTENT_CLAY |
||||
|
28: 0x813, # CONTENT_PAPYRUS |
||||
|
29: 0x814} # CONTENT_BOOKSHELF |
||||
|
|
||||
|
|
||||
|
def hex_to_int(h): |
||||
|
i = int(h, 16) |
||||
|
if(i > 2047): |
||||
|
i -= 4096 |
||||
|
return i |
||||
|
|
||||
|
|
||||
|
def hex4_to_int(h): |
||||
|
i = int(h, 16) |
||||
|
if(i > 32767): |
||||
|
i -= 65536 |
||||
|
return i |
||||
|
|
||||
|
|
||||
|
def int_to_hex3(i): |
||||
|
if(i < 0): |
||||
|
return "%03X" % (i + 4096) |
||||
|
else: |
||||
|
return "%03X" % i |
||||
|
|
||||
|
|
||||
|
def int_to_hex4(i): |
||||
|
if(i < 0): |
||||
|
return "%04X" % (i + 65536) |
||||
|
else: |
||||
|
return "%04X" % 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 |
||||
|
|
||||
|
def limit(i, l, h): |
||||
|
if(i > h): |
||||
|
i = h |
||||
|
if(i < l): |
||||
|
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)) |
||||
|
|
||||
|
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) |
||||
|
|
||||
|
usagetext = """minetestmapper.py [options] |
||||
|
-i/--input <world_path> |
||||
|
-o/--output <output_image.png> |
||||
|
--bgcolor <color> |
||||
|
--scalecolor <color> |
||||
|
--playercolor <color> |
||||
|
--origincolor <color> |
||||
|
--drawscale |
||||
|
--drawplayers |
||||
|
--draworigin |
||||
|
--region <xmin> <xmax> <zmin> <zmax> |
||||
|
--geometry <xmin>:<zmin>+<width>+<height> |
||||
|
--drawunderground |
||||
|
Color format: '#000000'""" |
||||
|
|
||||
|
def usage(): |
||||
|
print(usagetext) |
||||
|
|
||||
|
try: |
||||
|
opts, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input=", |
||||
|
"output=", "bgcolor=", "scalecolor=", "origincolor=", |
||||
|
"playercolor=", "draworigin", "drawplayers", "drawscale", |
||||
|
"drawunderground", "geometry=", "region="]) |
||||
|
except getopt.GetoptError as err: |
||||
|
# print help information and exit: |
||||
|
print(str(err)) # will print something like "option -a not recognized" |
||||
|
usage() |
||||
|
sys.exit(2) |
||||
|
|
||||
|
path = None |
||||
|
output = "map.png" |
||||
|
border = 0 |
||||
|
scalecolor = "black" |
||||
|
bgcolor = "white" |
||||
|
origincolor = "red" |
||||
|
playercolor = "red" |
||||
|
drawscale = False |
||||
|
drawplayers = False |
||||
|
draworigin = False |
||||
|
drawunderground = False |
||||
|
geometry_string = None |
||||
|
region_string = None |
||||
|
|
||||
|
for o, a in opts: |
||||
|
if o in ("-h", "--help"): |
||||
|
usage() |
||||
|
sys.exit() |
||||
|
elif o in ("-i", "--input"): |
||||
|
path = a |
||||
|
elif o in ("-o", "--output"): |
||||
|
output = a |
||||
|
elif o == "--bgcolor": |
||||
|
bgcolor = ImageColor.getrgb(a) |
||||
|
elif o == "--scalecolor": |
||||
|
scalecolor = ImageColor.getrgb(a) |
||||
|
elif o == "--playercolor": |
||||
|
playercolor = ImageColor.getrgb(a) |
||||
|
elif o == "--origincolor": |
||||
|
origincolor = ImageColor.getrgb(a) |
||||
|
elif o == "--drawscale": |
||||
|
drawscale = True |
||||
|
border = 40 |
||||
|
elif o == "--drawplayers": |
||||
|
drawplayers = True |
||||
|
elif o == "--draworigin": |
||||
|
draworigin = True |
||||
|
elif o == "--drawunderground": |
||||
|
drawunderground = True |
||||
|
elif o == "--geometry": |
||||
|
geometry_string = a |
||||
|
elif o == "--region": |
||||
|
region_string = a |
||||
|
else: |
||||
|
assert False, "unhandled option" |
||||
|
|
||||
|
nonchunky_xmin=-1500 |
||||
|
nonchunky_xmax=1500 |
||||
|
nonchunky_zmin=-1500 |
||||
|
nonchunky_zmax=1500 |
||||
|
|
||||
|
if geometry_string is not None: |
||||
|
parts = geometry_string.split("+") |
||||
|
if len(parts) == 3: |
||||
|
coords_string, width_string, height_string = parts |
||||
|
coords = coords_string.split(":") |
||||
|
if len(coords)==2: |
||||
|
x_string, z_string = coords |
||||
|
x = int(x_string) |
||||
|
z = int(z_string) |
||||
|
this_width = int(width_string) |
||||
|
this_height = int(height_string) |
||||
|
nonchunky_xmin = x |
||||
|
nonchunky_xmax = nonchunky_xmin + this_width |
||||
|
nonchunky_zmin = z |
||||
|
nonchunky_zmax = nonchunky_zmin + this_height |
||||
|
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)) |
||||
|
else: |
||||
|
print("ERROR: (Missing coordinates in '"+geometry_string+"' for geometry) Geometry should be in the form: x:z+width+height") |
||||
|
usage() |
||||
|
sys.exit() |
||||
|
else: |
||||
|
print("ERROR: (Incorrect value '"+geometry_string+"' for geometry) Geometry should be in the form: x:z+width+height") |
||||
|
usage() |
||||
|
sys.exit() |
||||
|
elif region_string is not None: |
||||
|
parts = region_string.split(" ") |
||||
|
if len(parts) == 4: |
||||
|
xmin_string, xmax_string, zmin_string, zmax_string = parts |
||||
|
nonchunky_xmin = int(xmin_string) |
||||
|
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)) |
||||
|
else: |
||||
|
print("ERROR: (Incorrect value '"+region_string+"' for region) Region should be in the form: xmin xmax zmin zmax") |
||||
|
usage() |
||||
|
sys.exit() |
||||
|
#answer=raw_input("press enter to continue") |
||||
|
sector_xmin = nonchunky_xmin / 16 |
||||
|
sector_xmax = nonchunky_xmax / 16 |
||||
|
sector_zmin = nonchunky_zmin / 16 |
||||
|
sector_zmax = nonchunky_zmax / 16 |
||||
|
|
||||
|
|
||||
|
if path is None: |
||||
|
print("Please select world path (eg. -i ../worlds/yourworld) (or use --help)") |
||||
|
sys.exit(1) |
||||
|
|
||||
|
if path[-1:] != "/" and path[-1:] != "\\": |
||||
|
path = path + "/" |
||||
|
|
||||
|
# Load color information for the blocks. |
||||
|
colors = {} |
||||
|
try: |
||||
|
f = file("colors.txt") |
||||
|
except IOError: |
||||
|
f = file(os.path.join(os.path.dirname(__file__), "colors.txt")) |
||||
|
for line in f: |
||||
|
values = string.split(line) |
||||
|
if len(values) < 4: |
||||
|
continue |
||||
|
identifier = values[0] |
||||
|
is_hex = True |
||||
|
for c in identifier: |
||||
|
if c not in "0123456789abcdefABCDEF": |
||||
|
is_hex = False |
||||
|
break |
||||
|
if is_hex: |
||||
|
colors[int(values[0], 16)] = ( |
||||
|
int(values[1]), |
||||
|
int(values[2]), |
||||
|
int(values[3])) |
||||
|
else: |
||||
|
colors[values[0]] = ( |
||||
|
int(values[1]), |
||||
|
int(values[2]), |
||||
|
int(values[3])) |
||||
|
f.close() |
||||
|
|
||||
|
#print("colors: "+repr(colors)) |
||||
|
#sys.exit(1) |
||||
|
|
||||
|
xlist = [] |
||||
|
zlist = [] |
||||
|
|
||||
|
# List all sectors to memory and calculate the width and heigth of the |
||||
|
# resulting picture. |
||||
|
|
||||
|
conn = None |
||||
|
cur = None |
||||
|
if os.path.exists(path + "map.sqlite"): |
||||
|
import sqlite3 |
||||
|
conn = sqlite3.connect(path + "map.sqlite") |
||||
|
cur = conn.cursor() |
||||
|
|
||||
|
cur.execute("SELECT `pos` FROM `blocks`") |
||||
|
while True: |
||||
|
r = cur.fetchone() |
||||
|
if not r: |
||||
|
break |
||||
|
|
||||
|
x, y, z = getIntegerAsBlock(r[0]) |
||||
|
|
||||
|
if x < sector_xmin or x >= sector_xmax: |
||||
|
continue |
||||
|
if z < sector_zmin or z >= sector_zmax: |
||||
|
continue |
||||
|
|
||||
|
xlist.append(x) |
||||
|
zlist.append(z) |
||||
|
|
||||
|
if os.path.exists(path + "sectors2"): |
||||
|
for filename in os.listdir(path + "sectors2"): |
||||
|
for filename2 in os.listdir(path + "sectors2/" + filename): |
||||
|
x = hex_to_int(filename) |
||||
|
z = hex_to_int(filename2) |
||||
|
if x < sector_xmin or x > sector_xmax: |
||||
|
continue |
||||
|
if z < sector_zmin or z > sector_zmax: |
||||
|
continue |
||||
|
xlist.append(x) |
||||
|
zlist.append(z) |
||||
|
|
||||
|
if os.path.exists(path + "sectors"): |
||||
|
for filename in os.listdir(path + "sectors"): |
||||
|
x = hex4_to_int(filename[:4]) |
||||
|
z = hex4_to_int(filename[-4:]) |
||||
|
if x < sector_xmin or x > sector_xmax: |
||||
|
continue |
||||
|
if z < sector_zmin or z > sector_zmax: |
||||
|
continue |
||||
|
xlist.append(x) |
||||
|
zlist.append(z) |
||||
|
|
||||
|
if len(xlist) == 0 or len(zlist) == 0: |
||||
|
print("At this chunk data does not exist.") |
||||
|
sys.exit(1) |
||||
|
|
||||
|
# Get rid of doubles |
||||
|
xlist, zlist = zip(*sorted(set(zip(xlist, zlist)))) |
||||
|
|
||||
|
minx = min(xlist) |
||||
|
minz = min(zlist) |
||||
|
maxx = max(xlist) |
||||
|
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) |
||||
|
|
||||
|
im = Image.new("RGB", (w + border, h + border), bgcolor) |
||||
|
draw = ImageDraw.Draw(im) |
||||
|
impix = im.load() |
||||
|
|
||||
|
stuff = {} |
||||
|
|
||||
|
unknown_node_names = [] |
||||
|
unknown_node_ids = [] |
||||
|
|
||||
|
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]) |
||||
|
elif version >= 20: |
||||
|
if mapdata[datapos] < 0x80: |
||||
|
return mapdata[datapos] |
||||
|
else: |
||||
|
return (mapdata[datapos] << 4) | (mapdata[datapos + 0x2000] >> 4) |
||||
|
elif 16 <= version < 20: |
||||
|
return TRANSLATION_TABLE.get(mapdata[datapos], mapdata[datapos]) |
||||
|
else: |
||||
|
raise Exception("Unsupported map format: " + str(version)) |
||||
|
|
||||
|
|
||||
|
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))) |
||||
|
else: |
||||
|
chunkxpos = xpos * 16 |
||||
|
chunkypos = ypos * 16 |
||||
|
chunkzpos = zpos * 16 |
||||
|
content = 0 |
||||
|
datapos = 0 |
||||
|
for (x, z) in reversed(pixellist): |
||||
|
for y in reversed(range(16)): |
||||
|
datapos = x + y * 16 + z * 256 |
||||
|
content = read_content(mapdata, version, datapos) |
||||
|
# Try to convert id to name |
||||
|
try: |
||||
|
content = id_to_name[content] |
||||
|
except KeyError: |
||||
|
pass |
||||
|
|
||||
|
if content_is_ignore(content): |
||||
|
pass |
||||
|
elif content_is_air(content): |
||||
|
pass |
||||
|
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) |
||||
|
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) |
||||
|
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)) |
||||
|
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)) |
||||
|
|
||||
|
|
||||
|
# Go through all sectors. |
||||
|
for n in range(len(xlist)): |
||||
|
#if n > 500: |
||||
|
# break |
||||
|
if n % 200 == 0: |
||||
|
nowtime = time.time() |
||||
|
dtime = nowtime - starttime |
||||
|
try: |
||||
|
n_per_second = 1.0 * n / dtime |
||||
|
except ZeroDivisionError: |
||||
|
n_per_second = 0 |
||||
|
if n_per_second != 0: |
||||
|
seconds_per_n = 1.0 / n_per_second |
||||
|
time_guess = seconds_per_n * 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)") |
||||
|
|
||||
|
xpos = xlist[n] |
||||
|
zpos = zlist[n] |
||||
|
|
||||
|
xhex = int_to_hex3(xpos) |
||||
|
zhex = int_to_hex3(zpos) |
||||
|
xhex4 = int_to_hex4(xpos) |
||||
|
zhex4 = int_to_hex4(zpos) |
||||
|
|
||||
|
sector1 = xhex4.lower() + zhex4.lower() |
||||
|
sector2 = xhex.lower() + "/" + zhex.lower() |
||||
|
|
||||
|
ylist = [] |
||||
|
|
||||
|
sectortype = "" |
||||
|
|
||||
|
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)) |
||||
|
while True: |
||||
|
r = cur.fetchone() |
||||
|
if not r: |
||||
|
break |
||||
|
pos = getIntegerAsBlock(r[0])[1] |
||||
|
ylist.append(pos) |
||||
|
sectortype = "sqlite" |
||||
|
try: |
||||
|
for filename in os.listdir(path + "sectors/" + sector1): |
||||
|
if(filename != "meta"): |
||||
|
pos = int(filename, 16) |
||||
|
if(pos > 32767): |
||||
|
pos -= 65536 |
||||
|
ylist.append(pos) |
||||
|
sectortype = "old" |
||||
|
except OSError: |
||||
|
pass |
||||
|
|
||||
|
if sectortype == "": |
||||
|
try: |
||||
|
for filename in os.listdir(path + "sectors2/" + sector2): |
||||
|
if(filename != "meta"): |
||||
|
pos = int(filename, 16) |
||||
|
if(pos > 32767): |
||||
|
pos -= 65536 |
||||
|
ylist.append(pos) |
||||
|
sectortype = "new" |
||||
|
except OSError: |
||||
|
pass |
||||
|
|
||||
|
if sectortype == "": |
||||
|
continue |
||||
|
|
||||
|
ylist.sort() |
||||
|
|
||||
|
# Make a list of pixels of the sector that are to be looked for. |
||||
|
pixellist = [] |
||||
|
water = {} |
||||
|
for x in range(16): |
||||
|
for z in range(16): |
||||
|
pixellist.append((x, z)) |
||||
|
water[(x, z)] = 0 |
||||
|
|
||||
|
# Go through the Y axis from top to bottom. |
||||
|
for ypos in reversed(ylist): |
||||
|
try: |
||||
|
#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,)) |
||||
|
r = cur.fetchone() |
||||
|
if not r: |
||||
|
continue |
||||
|
f = cStringIO.StringIO(r[0]) |
||||
|
else: |
||||
|
if sectortype == "old": |
||||
|
filename = path + "sectors/" + sector1 + "/" + yhex.lower() |
||||
|
else: |
||||
|
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)) |
||||
|
|
||||
|
# Check flags |
||||
|
is_underground = ((ord(flags) & 1) != 0) |
||||
|
day_night_differs = ((ord(flags) & 2) != 0) |
||||
|
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)) |
||||
|
|
||||
|
if version >= 22: |
||||
|
content_width = readU8(f) |
||||
|
params_width = readU8(f) |
||||
|
|
||||
|
# Node data |
||||
|
dec_o = zlib.decompressobj() |
||||
|
try: |
||||
|
mapdata = array.array("B", dec_o.decompress(f.read())) |
||||
|
except: |
||||
|
mapdata = [] |
||||
|
|
||||
|
# Reuse the unused tail of the file |
||||
|
f.close(); |
||||
|
f = cStringIO.StringIO(dec_o.unused_data) |
||||
|
#print("unused data: "+repr(dec_o.unused_data)) |
||||
|
|
||||
|
# zlib-compressed node metadata list |
||||
|
dec_o = zlib.decompressobj() |
||||
|
try: |
||||
|
metaliststr = array.array("B", dec_o.decompress(f.read())) |
||||
|
# And do nothing with it |
||||
|
except: |
||||
|
metaliststr = [] |
||||
|
|
||||
|
# Reuse the unused tail of the file |
||||
|
f.close(); |
||||
|
f = cStringIO.StringIO(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: |
||||
|
# mapblockobject_count |
||||
|
readU16(f) |
||||
|
|
||||
|
if version == 23: |
||||
|
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): |
||||
|
readU16(f) |
||||
|
readS32(f) |
||||
|
readS32(f) |
||||
|
|
||||
|
static_object_version = readU8(f) |
||||
|
static_object_count = readU16(f) |
||||
|
for i in range(0, static_object_count): |
||||
|
# u8 type (object type-id) |
||||
|
object_type = readU8(f) |
||||
|
# s32 pos_x_nodes * 10000 |
||||
|
pos_x_nodes = readS32(f)/10000 |
||||
|
# s32 pos_y_nodes * 10000 |
||||
|
pos_y_nodes = readS32(f)/10000 |
||||
|
# s32 pos_z_nodes * 10000 |
||||
|
pos_z_nodes = readS32(f)/10000 |
||||
|
# u16 data_size |
||||
|
data_size = readU16(f) |
||||
|
# u8[data_size] data |
||||
|
data = f.read(data_size) |
||||
|
|
||||
|
timestamp = readU32(f) |
||||
|
#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)) |
||||
|
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) |
||||
|
id_to_name[node_id] = name |
||||
|
|
||||
|
# Node timers |
||||
|
if version >= 25: |
||||
|
timer_size = readU8(f) |
||||
|
num = readU16(f) |
||||
|
for i in range(0,num): |
||||
|
readU16(f) |
||||
|
readS32(f) |
||||
|
readS32(f) |
||||
|
|
||||
|
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)) |
||||
|
sys.stdout.write("Block data: ") |
||||
|
for c in r[0]: |
||||
|
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(os.linesep) |
||||
|
traceback.print_exc() |
||||
|
|
||||
|
print("Drawing image") |
||||
|
# Drawing the picture |
||||
|
starttime = time.time() |
||||
|
n = 0 |
||||
|
for (x, z) in stuff.iterkeys(): |
||||
|
if n % 500000 == 0: |
||||
|
nowtime = time.time() |
||||
|
dtime = nowtime - starttime |
||||
|
try: |
||||
|
n_per_second = 1.0 * n / dtime |
||||
|
except ZeroDivisionError: |
||||
|
n_per_second = 0 |
||||
|
if n_per_second != 0: |
||||
|
listlen = len(stuff) |
||||
|
seconds_per_n = 1.0 / n_per_second |
||||
|
time_guess = seconds_per_n * listlen |
||||
|
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)") |
||||
|
n += 1 |
||||
|
|
||||
|
(r, g, b) = colors[stuff[(x, z)][1]] |
||||
|
|
||||
|
dnd = stuff[(x, z)][3] # day/night differs? |
||||
|
if not dnd and not drawunderground: |
||||
|
if stuff[(x, z)][2] > 0: # water |
||||
|
(r, g, b) = colors[CONTENT_WATER] |
||||
|
else: |
||||
|
continue |
||||
|
|
||||
|
# Comparing heights of a couple of adjacent blocks and changing |
||||
|
# brightness accordingly. |
||||
|
try: |
||||
|
c = stuff[(x, z)][1] |
||||
|
c1 = stuff[(x - 1, z)][1] |
||||
|
c2 = stuff[(x, z + 1)][1] |
||||
|
dnd1 = stuff[(x - 1, z)][3] |
||||
|
dnd2 = stuff[(x, z + 1)][3] |
||||
|
if not dnd: |
||||
|
d = -69 |
||||
|
elif not content_is_water(c1) and not content_is_water(c2) and \ |
||||
|
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 |
||||
|
d = ((y - y1) + (y - y2)) * 12 |
||||
|
else: |
||||
|
d = 0 |
||||
|
|
||||
|
if(d > 36): |
||||
|
d = 36 |
||||
|
|
||||
|
r = limit(r + d, 0, 255) |
||||
|
g = limit(g + d, 0, 255) |
||||
|
b = limit(b + d, 0, 255) |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
# Water |
||||
|
if(stuff[(x, z)][2] > 0): |
||||
|
r = int(r * .15 + colors[2][0] * .85) |
||||
|
g = int(g * .15 + colors[2][1] * .85) |
||||
|
b = int(b * .15 + colors[2][2] * .85) |
||||
|
|
||||
|
impix[x - minx * 16 + border, h - 1 - (z - minz * 16) + border] = (r, g, b) |
||||
|
|
||||
|
|
||||
|
if draworigin: |
||||
|
draw.ellipse((minx * -16 - 5 + border, h - minz * -16 - 6 + border, |
||||
|
minx * -16 + 5 + border, h - minz * -16 + 4 + border), |
||||
|
outline=origincolor) |
||||
|
|
||||
|
font = ImageFont.load_default() |
||||
|
|
||||
|
if drawscale: |
||||
|
draw.text((24, 0), "X", font=font, fill=scalecolor) |
||||
|
draw.text((2, 24), "Z", font=font, fill=scalecolor) |
||||
|
|
||||
|
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) |
||||
|
draw.line((minx * -16 + n * 16 + border, 0, |
||||
|
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) |
||||
|
draw.line((0, h - 1 - (n * 16 - minz * 16) + border, border - 1, |
||||
|
h - 1 - (n * 16 - minz * 16) + border), fill=scalecolor) |
||||
|
|
||||
|
if drawplayers: |
||||
|
try: |
||||
|
for filename in os.listdir(path + "players"): |
||||
|
f = file(path + "players/" + filename) |
||||
|
lines = f.readlines() |
||||
|
name = "" |
||||
|
position = [] |
||||
|
for line in lines: |
||||
|
p = string.split(line) |
||||
|
if p[0] == "name": |
||||
|
name = p[2] |
||||
|
print(filename + ": name = " + name) |
||||
|
if p[0] == "position": |
||||
|
position = string.split(p[2][1:-1], ",") |
||||
|
print(filename + ": position = " + p[2]) |
||||
|
if len(name) > 0 and len(position) == 3: |
||||
|
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) |
||||
|
draw.text((x + 2 + border, z + 2 + border), name, |
||||
|
font=font, fill=playercolor) |
||||
|
f.close() |
||||
|
except OSError: |
||||
|
pass |
||||
|
|
||||
|
print("Saving") |
||||
|
im.save(output) |
||||
|
|
||||
|
if unknown_node_names: |
||||
|
sys.stdout.write("Unknown node names:") |
||||
|
for name in unknown_node_names: |
||||
|
sys.stdout.write(" "+name) |
||||
|
sys.stdout.write(os.linesep) |
||||
|
if unknown_node_ids: |
||||
|
sys.stdout.write("Unknown node ids:") |
||||
|
for node_id in unknown_node_ids: |
||||
|
sys.stdout.write(" "+str(hex(node_id))) |
||||
|
sys.stdout.write(os.linesep) |
||||
|
|
Loading…
Reference in new issue