/*
 * mtsedit/schemimp.c
 *
 * Copyright (C) 2019 bzt (bztsrc@gitlab)
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * @brief Import various non-MTS schematic formats
 *
 */

#include "main.h"

/**
 * match name
 */
int namematch(char *a, char *b)
{
    int la, lb, i;
    if(!a || !*a || !b || !*b) return 0;
    if(!strcmp(a, b)) return 1;
    la = strlen(a); lb = strlen(b);
    for(i = 0; i < lb - la + 1; i++) {
        if((!i || b[i-1] == '/') && !memcmp(a, b + i, la) && (!b[i + la] || b[i + la] == '/'))
            return 1;
    }
    return 0;
}

/**
 * Load a Minecraft NBT Schematic file
 */
/* parse an integer, the only dynamic type we are interested in. */
#define SCHEM_GETINT(v,t) do{switch(t){case 1:v=d[0];d++;break;case 2:v=(d[0]<<8)|d[1];d+=2;break; \
    case 3:v=(d[0]<<24)|(d[1]<<16)|(d[2]<<8)|d[3];d+=4;break;}}while(0)
void schem_load(unsigned char *data, unsigned int size)
{
    int i, j, k, l, x, y, z, g = 0, min_x = 0, min_y = 0, min_z = 0, tr[1024];
    char name[256], rep[1024], rot[1024];
    unsigned char *d, *s, *blk, *blkd = NULL, *blkp = NULL, *end = data + size;
    unsigned int n;

    /* we don't care about that overcomplicated NBT mess */
    for(d = data, blk = NULL, i = 0; d + 12 < end; d++) {
        if(!memcmp(d, "\000\006Height", 8)) { d += 8; SCHEM_GETINT(mts_y, d[-9]); d--; } else
        if(!memcmp(d, "\000\006Length", 8)) { d += 8; SCHEM_GETINT(mts_z, d[-9]); d--; } else
        if(!memcmp(d, "\000\005Width", 7)) { d += 7; SCHEM_GETINT(mts_x, d[-8]); d--; } else
        if(!memcmp(d, "\000\009WEOffsetY", 11)) { d += 11; SCHEM_GETINT(g, d[-12]); d--; } else
        if(!memcmp(d, "\007\000\004Data", 7)) { d += 7; SCHEM_GETINT(i, 3); blkd = d; d += i; d--; } else
        if(!memcmp(d, "\007\000\006Blocks", 9)) { d += 9; SCHEM_GETINT(l, 3); blk = d; d += l; d--; } else
        if(!memcmp(d, "\012\000\007Palette", 10)) { d += 10; blkp = d; } else
        if(!memcmp(d, "\007\000\011BlockData", 12)) { d += 12; SCHEM_GETINT(l, 3); blk = d; d += l; d--; }
    }
    /* we handle three variants here:
     * 1. classic: Blocks chunk with block data, and Data chunk with meta-data
     * 2. sponge: Palette chunk, BlockData chunk with block data
     * 3. latest (Worldedit 7.3.0): Blocks chunk has no data (type 10), Palette chunk, Data chunk with the actual block data
     */
    if(!blk && blkp && blkd && i) { blk = blkd; blkd = NULL; l = i; }

    if(!blk || !mts_y || !mts_z || !mts_x || mts_y > 255 || mts_z > 255 || mts_x > 255 || l < mts_y * mts_z * mts_x) {
        mts_y = mts_z = mts_x = 0;
        return;
    }

    memset(tr, 0xff, sizeof(tr));
    memset(rep, 0, sizeof(rep));
    memset(rot, 0, sizeof(rot));
    if(!blkp) {
        tr[0] = 0;
        for(i = 1; i < numblocks; i++)
            for(j = 0; j < 4; j++)
                if(blocks[i].blockids[j])
                    tr[blocks[i].blockids[j]] = i;
        rot[0] = 0; rot[1] = 2; rot[2] = 1; rot[3] = 3;
    } else {
        for(d = blkp; *d > 0 && *d < 4;) {
            j = *d++; d++; l = *d++; s = d; d += l;
            memcpy(name, s, l); name[l] = 0;
            SCHEM_GETINT(k, j);
            for(l = 0; name[l]; l++) if(name[l] == '[') {
                if(strstr(name + l, "=north")) rot[k] = 0; else
                if(strstr(name + l, "=east")) rot[k] = 1; else
                if(strstr(name + l, "=west")) rot[k] = 2; else
                if(strstr(name + l, "=south")) rot[k] = 3;
                name[l] = 0;
                break;
            }
            for(i = l = 0; i < numblocks; i++)
                if((blocks[i].name && (!strcmp(blocks[i].name, name) || (!strncmp(name, "minecraft:", 10) &&
                    strlen(blocks[i].name) > 11 && !strcasecmp(blocks[i].name + 10, name)))) ||
                    (blocks[i].blocknames && namematch(name, blocks[i].blocknames[0]))) { tr[k] = i; l = 1; break; }
            if(!l) {
                fprintf(stderr, "mtsedit: %s: %s '%s'\r\n", mtsfile, lang[ERR_NODE], name);
                j = numblocks++;
                blocks = (mtsblock_t*)realloc(blocks, numblocks * sizeof(mtsblock_t));
                if(!blocks) error(lang[ERR_MEM]);
                memset(&blocks[j], 0, sizeof(mtsblock_t));
                blocks[j].name = (char*)malloc(strlen(name) + 1);
                if(!blocks[j].name) error(lang[ERR_MEM]);
                strcpy(blocks[j].name, name);
            }
        }
    }
    blocks[0].numref = l;
    min_x = 127 - mts_x / 2;
    min_y = 127 - mts_y / 2;
    min_z = 127 - mts_z / 2;
    /* now that we know the dimensions, and the blocks data, we can parse */
    for(d = blk, i = y = 0; y < mts_y; y++)
        for(z = 0; z < mts_z; z++)
            for(x = 0; x < mts_x; x++, i++) {
                if(!blkp)
                    n = *d++;
                else {
                    /* this is a f*cked up non-documented varint encoding...
                     * see https://github.com/EngineHub/WorldEdit/blob/master/worldedit-core/src/main/java/com/sk89q/worldedit/extent/clipboard/io/SpongeSchematicReader.java#L259 */
                    for(n = 0, j = 0; j < 21; d++, j += 7) {
                        n |= (*d & 0x7F) << j;
                        if(!(*d & 0x80)) { d++; break; }
                    }
                }
                if(n < 1024 && tr[n] != -1) {
                    layerprob[y+min_y] = 127;
                    nodes[y+min_y][255-(x+min_x)][z+min_z].param0 = tr[n];
                    nodes[y+min_y][255-(x+min_x)][z+min_z].param1 = !tr[n] ? 0 : 127;
                    if(blkp)
                        nodes[y+min_y][255-(x+min_x)][z+min_z].param2 = rot[tr[n]];
                    else if(blkd)
                        nodes[y+min_y][255-(x+min_x)][z+min_z].param2 = rot[blkd[i] & 3];
                    blocks[tr[n]].numref++;
                } else if(!blkp && !*d) {
                    if(n >= sizeof(rep) || !rep[n])
                        fprintf(stderr, "mtsedit: %s: %s %d\r\n", mtsfile, lang[ERR_NODE], n);
                    if(n < sizeof(rep))
                        rep[n] = 1;
                }
            }
    mts_getbounds(0, NULL, NULL);
    mts_x = max - mix + 1;
    mts_y = may - miy + 1;
    mts_z = maz - miz + 1;
    gndlayer = miy - g;
    status = lang[LOADED];
}

/**
 * Load a blueprint from a PNG file (or voxelize any other PNG image)
 */
void bprint_load(unsigned char *data, unsigned int size)
{
    stbi__context s;
    stbi__result_info ri;
    int w = 0, h = 0, xs = 1, ys = 0, yg = 0, min_x = 0, min_y = 0, min_z = 0, len, x, y, z;
    uint32_t *d;

    mts_y = mts_z = mts_x = gndlayer = 0;
    s.read_from_callbacks = 0;
    s.img_buffer = s.img_buffer_original = data;
    s.img_buffer_end = s.img_buffer_original_end = data + size;
    ri.bits_per_channel = 8;
    d = (uint32_t*)stbi__png_load(&s, &w, &h, &len, 0, &ri);
    if(!d || len != 4) return;

    /* detect dimensions */
    for(y = 0; y < h; y++) {
        if(d[y*w] == 0xFF000000 || d[y*w] == 0xFF0000FF) {
            if(!mts_y) {
                ys = y;
                for(mts_z=1; mts_z+1 < w-1 && d[y*w+mts_z+1] != 0xFF0000FF && d[y*w+mts_z+1] != theme[THEME_BP_BG]; mts_z++);
            } else if(mts_y == 1) mts_x = y - ys - 1;
            if(d[y*w] == 0xFF0000FF) yg = mts_y;
            mts_y++;
        }
    }
    if(!mts_x) mts_x = h - ys - 2;
    if(!mts_y) { ys = yg = xs = 0; mts_y = 1; mts_z = w; mts_x = h; }
    if(mts_x < 1 || mts_y < 1 || mts_z < 1) { mts_y = mts_z = mts_x = 0; return; }
    if(mts_y > 255) mts_y = 255;
    if(mts_z > 255) mts_z = 255;
    if(mts_x > 255) mts_x = 255;
    blocks[0].numref = mts_y * mts_z * mts_x;
    min_x = 127 - mts_x / 2;
    min_y = 127 + mts_y / 2;
    min_z = 127 - mts_z / 2;
    gndlayer = min_y - yg;

    /* read in the layers */
    for(y = 0; y < mts_y; y++)
        for(x = 0; x < mts_x && (y*(mts_x+1)+ys+x) < h; x++)
            for(z = 0; z < mts_z; z++) {
                nodes[min_y-y][mts_z+min_z-z][x+min_x].param0 = color2blockid(d[(y*(mts_x+1)+ys+x)*w + z + xs]);
                if(nodes[min_y-y][mts_z+min_z-z][x+min_x].param0) {
                    layerprob[min_y-y] = 127;
                    nodes[min_y-y][mts_z+min_z-z][x+min_x].param1 = 127;
                    blocks[nodes[min_y-y][mts_z+min_z-z][x+min_x].param0].numref++;
                    blocks[0].numref--;
                }
            }
    status = lang[LOADED];
}

/**
 * Load a Model 3D with voxel image
 */
void m3d_load(unsigned char *data, unsigned int size)
{
    unsigned char *f = NULL, *ch, *s, *e = data + size;
    char *strtbl, *n;
    int i, j, k, l, m, ci_s, si_s, sk_s, vd_s, vp_s, nt = 0, *palref;
    int min_x = 0, min_y = 0, min_z = 0, mi_x = 65536, mi_y = 65536, mi_z = 65536, ma_x = 0, ma_y = 0, ma_z = 0, x, y, z;
    int sx, sy, sz, px, py, pz;
    uint32_t c, *cmap = NULL, *tr = NULL;

    data += 8;
    size -= 8;
    if(!memcmp(data, "PRVW", 4)) {
        size -= *((uint32_t*)(data + 4));
        data += *((uint32_t*)(data + 4));
    }
    if(memcmp(data, "HEAD", 4)) {
        data = f = (unsigned char *)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)data, size, 4096, &l, 1);
        if(!data || !l || memcmp(data, "HEAD", 4)) {
            if(data) free(data);
            return;
        }
        e = data + l;
    }
    strtbl = (char*)data + 16;
    si_s = 1 << ((data[12] >> 4) & 3);
    ci_s = 1 << ((data[12] >> 6) & 3);
    sk_s = 1 << ((data[13] >> 6) & 3);
    vd_s = 1 << ((data[14] >> 6) & 3);
    vp_s = 1 << ((data[15] >> 0) & 3);
    if(ci_s == 8) ci_s = 0;
    if(sk_s == 8) sk_s = 0;
    if(vd_s == 8 || vp_s > 2) return;

    /* detect dimensions and get color and node palettes */
    ch = data;
    while(ch < e && memcmp(ch, "OMD3", 4)) {
        s = ch;
        l = *((uint32_t*)(ch + 4));
        ch += l;
        if(l < 8 || ch >= e) { ma_x = mi_x = nt = 0; break; }
        l -= 8;
        if(!memcmp(s, "CMAP", 4)) { cmap = (uint32_t*)(s + 8); } else
        if(!memcmp(s, "VOXT", 4)) {
            s += 8;
            nt = l / (ci_s + si_s + 3 + sk_s);
            tr = (uint32_t*)malloc(nt * sizeof(uint32_t));
            if(!tr) error(lang[ERR_MEM]);
            palref = (int*)malloc(numpalettes * sizeof(int));
            if(!palref) error(lang[ERR_MEM]);
            memset(palref, 0, numpalettes * sizeof(int));
            /* get voxel types */
            for(i = 0; i < nt; i++) {
                c = 0;
                switch(ci_s) {
                    case 1:  c = cmap ? cmap[s[0]] : 0; s++; break;
                    case 2:  c = cmap ? cmap[*((uint16_t*)s)] : 0; s += 2; break;
                    case 4:  c = *((uint32_t*)s); s += 4; break;
                }
                j = 0;
                switch(si_s) {
                    case 1:  j = s[0]; s++; break;
                    case 2:  j = *((uint16_t*)s); s += 2; break;
                    case 4:  j = *((uint32_t*)s); s += 4; break;
                }
                /* if voxel type name given, match that */
                if(j) {
                    n = strtbl + j;
                    tr[i] = numblocks;
                    for(k = 0; k < numblocks; k++)
                        for(m = 0; m < numpalettes + 3; m++)
                            if((!blocks[k].blocknames && !strcmp(blocks[k].name, n)) ||
                                (blocks[k].blocknames && namematch(n, blocks[k].blocknames[m]))) {
                                    if(k)
                                        switch(m) {
                                            case 0: break;
                                            case 1: blocks[k].dobiome = 1; savebiome = 1; break;
                                            case 2: if(!memcmp(n, "mapgen", 6)) { savemapgen = 1; } break;
                                            default: palref[m - 3]++; break;
                                        }
                                    tr[i] = k;
                                    break;
                                }
                    if(tr[i] == (uint32_t)numblocks) {
                        if(c) tr[i] = color2blockid(c);
                        else {
                            fprintf(stderr, "mtsedit: %s: %s %d '%s'\r\n", mtsfile, lang[ERR_NODE], i, n);
                            j = numblocks++;
                            blocks = (mtsblock_t*)realloc(blocks, numblocks * sizeof(mtsblock_t));
                            if(!blocks) error(lang[ERR_MEM]);
                            memset(&blocks[j], 0, sizeof(mtsblock_t));
                            blocks[j].name = (char*)malloc(strlen(n) + 1);
                            if(!blocks[j].name) error(lang[ERR_MEM]);
                            strcpy(blocks[j].name, n);
                        }
                    }
                } else
                /* fallback to best color match */
                if(c) tr[i] = color2blockid(c);
                else tr[i] = 0;
                tr[i] |= s[0] << 24;
                /* skip over additional attributes */
                s += 2;
                j = *s;
                s += 1 + sk_s + j * (2 + si_s);
            }
            /* pick the node palette which has the most node reference */
            for(i = savepal = 0; i < numpalettes; i++)
                if(palref[i] > palref[savepal]) savepal = i;
            free(palref);
        } else
        if(!memcmp(s, "VOXD", 4)) {
            s += 8 + si_s;
            px = py = pz = sx = sy = sz = 0;
            switch(vd_s) {
                case 1:
                    px = (int8_t)s[0]; py = (int8_t)s[1]; pz = (int8_t)s[2];
                    sx = (int8_t)s[3]; sy = (int8_t)s[4]; sz = (int8_t)s[5];
                break;
                case 2:
                    px = *((int16_t*)(s+0)); py = *((int16_t*)(s+2)); pz = *((int16_t*)(s+4));
                    sx = *((int16_t*)(s+6)); sy = *((int16_t*)(s+8)); sz = *((int16_t*)(s+10));
                break;
                default: return; break;
            }
            if(px < mi_x) { mi_x = px; } if(px+sx > ma_x) { ma_x = px+sx; }
            if(py < mi_y) { mi_y = py; } if(py+sy > ma_y) { ma_y = py+sy; }
            if(pz < mi_z) { mi_z = pz; } if(pz+sz > ma_z) { ma_z = pz+sz; }
        }
    }
    mts_x = ma_x - mi_x;
    mts_y = ma_y - mi_y;
    mts_z = ma_z - mi_z;
    if(mts_y > 255) mts_y = 255;
    if(mts_z > 255) mts_z = 255;
    if(mts_x > 255) mts_x = 255;
    if(!nt || mts_x < 1 || mts_y < 1 || mts_z < 1) {
        mts_y = mts_z = mts_x = 0;
        if(f) free(f);
        if(tr) free(tr);
        return;
    }
    blocks[0].numref = mts_y * mts_z * mts_x;
    min_x = 127 - mts_x / 2;
    min_y = 127 - mts_y / 2;
    min_z = 127 - mts_z / 2;
    gndlayer = min_y - mi_y;

    /* read in the layers */
    ch = data;
    while(ch < e && memcmp(ch, "OMD3", 4)) {
        s = ch;
        l = *((uint32_t*)(ch + 4));
        ch += l;
        if(!memcmp(s, "VOXD", 4)) {
            s += 8 + si_s;
            px = py = pz = sx = sy = sz = 0;
            switch(vd_s) {
                case 1:
                    px = (int8_t)s[0]; py = (int8_t)s[1]; pz = (int8_t)s[2];
                    sx = (int8_t)s[3]; sy = (int8_t)s[4]; sz = (int8_t)s[5];
                    s += 6;
                break;
                case 2:
                    px = *((int16_t*)(s+0)); py = *((int16_t*)(s+2)); pz = *((int16_t*)(s+4));
                    sx = *((int16_t*)(s+6)); sy = *((int16_t*)(s+8)); sz = *((int16_t*)(s+10));
                    s += 12;
                break;
            }
            px += min_x-mi_x; py += min_y-mi_y; pz += min_z-mi_z;
            k = (255 - *s) >> 1;
            for(y = 0; y < sy; y++)
                if(py+y >= 0 && py+y < 256)
                    layerprob[py+y] = k;
            k = sx * sz; x = y = z = 0;
            for(s += 2, i = 0; s < ch;) {
                l = ((*s++) & 0x7F) + 1;
                if(s[-1] & 0x80) {
                    if(vp_s == 1) { m = *s++; } else { m = *((uint16_t*)s); s += 2; }
                    for(j = 0; j < l; j++, i++) {
                        if(m >= 0 && m < nt && py+y >= 0 && py+y < 256 && pz+z >= 0 && pz+z < 256 && px+x >= 0 && px+x < 256) {
                            nodes[py+y][pz+z][px+x].param0 = tr[m] & 0xFFFFFF;
                            nodes[py+y][pz+z][px+x].param1 = tr[m] ? 127 : 0;
                            nodes[py+y][pz+z][px+x].param2 = (tr[m] >> 24) & 0xFF;
                            blocks[nodes[py+y][py+z][px+x].param0].numref++;
                            blocks[0].numref--;
                        }
                        x++; if(x >= sx) { x = 0; z++; if(z >= sz) { z = 0; y++; } }
                    }
                } else {
                    for(j = 0; j < l; j++, i++) {
                        if(vp_s == 1) { m = *s++; } else { m = *((uint16_t*)s); s += 2; }
                        if(m >= 0 && m < nt && py+y >= 0 && py+y < 256 && pz+z >= 0 && pz+z < 256 && px+x >= 0 && px+x < 256) {
                            nodes[py+y][pz+z][px+x].param0 = tr[m] & 0xFFFFFF;
                            nodes[py+y][pz+z][px+x].param1 = tr[m] ? 127 : 0;
                            nodes[py+y][pz+z][px+x].param2 = (tr[m] >> 24) & 0xFF;
                            blocks[nodes[py+y][py+z][px+x].param0].numref++;
                            blocks[0].numref--;
                        }
                        x++; if(x >= sx) { x = 0; z++; if(z >= sz) { z = 0; y++; } }
                    }
                }
            }
        }
    }

    if(f) free(f);
    if(tr) free(tr);
    status = lang[LOADED];
}

/**
 * Load a Tiled TMX file
 */
void tmx_load(unsigned char *data, unsigned int size)
{
    unsigned char *s, *e = data + size;
    int i, j, k, min_x = 0, min_y = 0, min_z = 0, x, y, z;

    /* detect dimensions */
    for(s = data; *s && s < e; s++)
        if(!memcmp(s, "<layer", 6)) {
            if(!mts_y)
                for(; *s && *s != '>'; s++) {
                    if(!memcmp(s, "width=", 6)) {
                        s += 6; if(*s == '\"') s++;
                        mts_x = atoi((char*)s);
                    }
                    if(!memcmp(s, "height=", 7)) {
                        s += 7; if(*s == '\"') s++;
                        mts_z = atoi((char*)s);
                    }
                }
            mts_y++;
        }
    if(mts_x < 1 || mts_y < 1 || mts_z < 1) { mts_y = mts_z = mts_x = 0; return; }
    if(mts_y > 255) mts_y = 255;
    if(mts_z > 255) mts_z = 255;
    if(mts_x > 255) mts_x = 255;
    blocks[0].numref = mts_y * mts_z * mts_x;
    min_x = 127 - mts_x / 2;
    min_y = 127 + mts_y / 2;
    min_z = 127 - mts_z / 2;
    gndlayer = min_y;

    /* read in the layers */
    for(s = data, y = 0; *s && y < 255; s++)
        if(!memcmp(s, "<layer", 6)) {
            for(; *s && memcmp(s, "<data", 5); s++);
            for(; *s && *s != '>'; s++);
            for(; *s && (*s == '>' || *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'); s++);
            for(x = z = 0; *s && *s != '<'; s++) {
                if(*s == '\r' || *s == '\t' || *s == ' ' || *s == ',') continue;
                if(*s == '\n') { x = 0; z++; continue; }
                j = atoi((char*)s);
                for(; *s && *s >= '0' && *s <= '9'; s++);
                for(i = 0, k = -1; i < numblocks; i++)
                    if(j == blocks[i].blockids[0]) { k = i; break; }
                if(k != -1) {
                    if(k && x < 255 && z < 255) {
                        nodes[min_y-y][z+min_z][x+min_x].param0 = k;
                        layerprob[min_y-y] = 127;
                        nodes[min_y-y][z+min_z][x+min_x].param1 = 127;
                        blocks[nodes[min_y-y][z+min_z][x+min_x].param0].numref++;
                        blocks[0].numref--;
                    }
                } else
                    fprintf(stderr, "mtsedit: %s: %s %d\r\n", mtsfile, lang[ERR_NODE], j);
            }
            y++;
        }
    status = lang[LOADED];
}

/**
 * Load a Goxel GOX file
 */
void gox_load(unsigned char *data, unsigned int size)
{
    stbi__context sc;
    stbi__result_info ri;
    int i, j, l, min_x = 0, min_y = 0, min_z = 0, mi_x = 65536, mi_y = 65536, mi_z = 65536, ma_x = 0, ma_y = 0, ma_z = 0, x, y, z;
    int k, nb = 0, px, py, pz;
    unsigned char *s, *e = data + size, *c;
    uint32_t p, **blks = NULL;

    /* detect dimensions and uncompress blocks */
    for(s = data + 8, nb = 0; s < e; s++) {
        if(!memcmp(s, "BL16", 4)) {
            blks = (uint32_t**)realloc(blks, (nb+1) * sizeof(uint32_t*));
            if(!blks) error(lang[ERR_MEM]);
            j = *((uint32_t*)(s + 4));
            sc.read_from_callbacks = 0;
            sc.img_buffer = sc.img_buffer_original = s + 8;
            sc.img_buffer_end = sc.img_buffer_original_end = s + 8 + j;
            ri.bits_per_channel = 8;
            blks[nb] = (uint32_t*)stbi__png_load(&sc, &i, &j, &l, 0, &ri);
            nb++;
        } else
        if(!memcmp(s, "LAYR", 4)) {
            l = *((uint32_t*)(s + 8));
            for(c = s + 12, i = 0; i < l; i++, c += 20) {
                px = *((int*)(c + 4)); py = *((int*)(c + 8)); pz = *((int*)(c + 12));
                if(px < mi_x) { mi_x = px; } if(px+16 > ma_x) { ma_x = px+16; }
                if(py < mi_y) { mi_y = py; } if(py+16 > ma_y) { ma_y = py+16; }
                if(pz < mi_z) { mi_z = pz; } if(pz+16 > ma_z) { ma_z = pz+16; }
            }
        }
        s += *((uint32_t*)(s + 4)) + 11;
    }
    mts_x = ma_x - mi_x;
    mts_y = ma_y - mi_y;
    mts_z = ma_z - mi_z;
    if(mts_y > 255) mts_y = 255;
    if(mts_z > 255) mts_z = 255;
    if(mts_x > 255) mts_x = 255;
    if(!blks || mts_x < 1 || mts_y < 1 || mts_z < 1) { mts_y = mts_z = mts_x = 0; return; }
    blocks[0].numref = mts_y * mts_z * mts_x;
    min_x = 127 - mts_x / 2;
    min_y = 127 - mts_y / 2;
    min_z = 127 - mts_z / 2;

    /* read in the layers (possibly from multiple chunks) */
    for(s = data + 8; s < e; s++) {
        if(!memcmp(s, "LAYR", 4)) {
            l = *((uint32_t*)(s + 8));
            for(c = s + 12, i = 0; i < l; i++, c += 20) {
                j = *((int*)(c));
                px = *((int*)(c+4)) - mi_x + min_x; py = *((int*)(c+8)) - mi_y + min_y; pz = *((int*)(c+12)) - mi_z + min_z;
                for(k = z = 0; z < 16; z++)
                    for(y = 0; y < 16; y++)
                        for(x = 0; x < 16; x++, k++)
                            if(j >=0 && j < nb && blks[j][k] && px+x >= 0 && px+x < 256 && py+y >= 0 && py+y < 256 &&
                                pz+z >= 0 && pz+z < 256) {
                                    p = color2blockid(blks[j][k]);
                                    if(p) {
                                        nodes[pz+z][py+y][px+x].param0 = p;
                                        layerprob[pz+z] = 127;
                                        nodes[pz+z][py+y][px+x].param1 = 127;
                                        blocks[nodes[pz+z][py+y][pz+x].param0].numref++;
                                        blocks[0].numref--;
                                    }
                            }
            }
        }
        s += *((uint32_t*)(s + 4)) + 11;
    }

    for(i = 0; i < nb; i++)
        if(blks[i]) free(blks[i]);
    free(blks);

    bound_valid = 0;
    mts_getbounds(1, NULL, NULL);
    mts_flip();
    mts_x = max - mix + 1;
    mts_y = may - miy + 1;
    mts_z = maz - miz + 1;
    gndlayer = miy;
    status = lang[LOADED];
}

/**
 * Load a Magicavoxel VOX file
 */
static uint32_t vox_default_palette[256] = {
    0x00000000, 0xffffffff, 0xffccffff, 0xff99ffff, 0xff66ffff, 0xff33ffff, 0xff00ffff, 0xffffccff, 0xffccccff, 0xff99ccff, 0xff66ccff, 0xff33ccff, 0xff00ccff, 0xffff99ff, 0xffcc99ff, 0xff9999ff,
    0xff6699ff, 0xff3399ff, 0xff0099ff, 0xffff66ff, 0xffcc66ff, 0xff9966ff, 0xff6666ff, 0xff3366ff, 0xff0066ff, 0xffff33ff, 0xffcc33ff, 0xff9933ff, 0xff6633ff, 0xff3333ff, 0xff0033ff, 0xffff00ff,
    0xffcc00ff, 0xff9900ff, 0xff6600ff, 0xff3300ff, 0xff0000ff, 0xffffffcc, 0xffccffcc, 0xff99ffcc, 0xff66ffcc, 0xff33ffcc, 0xff00ffcc, 0xffffcccc, 0xffcccccc, 0xff99cccc, 0xff66cccc, 0xff33cccc,
    0xff00cccc, 0xffff99cc, 0xffcc99cc, 0xff9999cc, 0xff6699cc, 0xff3399cc, 0xff0099cc, 0xffff66cc, 0xffcc66cc, 0xff9966cc, 0xff6666cc, 0xff3366cc, 0xff0066cc, 0xffff33cc, 0xffcc33cc, 0xff9933cc,
    0xff6633cc, 0xff3333cc, 0xff0033cc, 0xffff00cc, 0xffcc00cc, 0xff9900cc, 0xff6600cc, 0xff3300cc, 0xff0000cc, 0xffffff99, 0xffccff99, 0xff99ff99, 0xff66ff99, 0xff33ff99, 0xff00ff99, 0xffffcc99,
    0xffcccc99, 0xff99cc99, 0xff66cc99, 0xff33cc99, 0xff00cc99, 0xffff9999, 0xffcc9999, 0xff999999, 0xff669999, 0xff339999, 0xff009999, 0xffff6699, 0xffcc6699, 0xff996699, 0xff666699, 0xff336699,
    0xff006699, 0xffff3399, 0xffcc3399, 0xff993399, 0xff663399, 0xff333399, 0xff003399, 0xffff0099, 0xffcc0099, 0xff990099, 0xff660099, 0xff330099, 0xff000099, 0xffffff66, 0xffccff66, 0xff99ff66,
    0xff66ff66, 0xff33ff66, 0xff00ff66, 0xffffcc66, 0xffcccc66, 0xff99cc66, 0xff66cc66, 0xff33cc66, 0xff00cc66, 0xffff9966, 0xffcc9966, 0xff999966, 0xff669966, 0xff339966, 0xff009966, 0xffff6666,
    0xffcc6666, 0xff996666, 0xff666666, 0xff336666, 0xff006666, 0xffff3366, 0xffcc3366, 0xff993366, 0xff663366, 0xff333366, 0xff003366, 0xffff0066, 0xffcc0066, 0xff990066, 0xff660066, 0xff330066,
    0xff000066, 0xffffff33, 0xffccff33, 0xff99ff33, 0xff66ff33, 0xff33ff33, 0xff00ff33, 0xffffcc33, 0xffcccc33, 0xff99cc33, 0xff66cc33, 0xff33cc33, 0xff00cc33, 0xffff9933, 0xffcc9933, 0xff999933,
    0xff669933, 0xff339933, 0xff009933, 0xffff6633, 0xffcc6633, 0xff996633, 0xff666633, 0xff336633, 0xff006633, 0xffff3333, 0xffcc3333, 0xff993333, 0xff663333, 0xff333333, 0xff003333, 0xffff0033,
    0xffcc0033, 0xff990033, 0xff660033, 0xff330033, 0xff000033, 0xffffff00, 0xffccff00, 0xff99ff00, 0xff66ff00, 0xff33ff00, 0xff00ff00, 0xffffcc00, 0xffcccc00, 0xff99cc00, 0xff66cc00, 0xff33cc00,
    0xff00cc00, 0xffff9900, 0xffcc9900, 0xff999900, 0xff669900, 0xff339900, 0xff009900, 0xffff6600, 0xffcc6600, 0xff996600, 0xff666600, 0xff336600, 0xff006600, 0xffff3300, 0xffcc3300, 0xff993300,
    0xff663300, 0xff333300, 0xff003300, 0xffff0000, 0xffcc0000, 0xff990000, 0xff660000, 0xff330000, 0xff0000ee, 0xff0000dd, 0xff0000bb, 0xff0000aa, 0xff000088, 0xff000077, 0xff000055, 0xff000044,
    0xff000022, 0xff000011, 0xff00ee00, 0xff00dd00, 0xff00bb00, 0xff00aa00, 0xff008800, 0xff007700, 0xff005500, 0xff004400, 0xff002200, 0xff001100, 0xffee0000, 0xffdd0000, 0xffbb0000, 0xffaa0000,
    0xff880000, 0xff770000, 0xff550000, 0xff440000, 0xff220000, 0xff110000, 0xffeeeeee, 0xffdddddd, 0xffbbbbbb, 0xffaaaaaa, 0xff888888, 0xff777777, 0xff555555, 0xff444444, 0xff222222, 0xff111111,
};
void vox_load(unsigned char *data, unsigned int size)
{
    unsigned char *s, *e = data + size;
    uint32_t *pal = vox_default_palette;
    int i, min_x = 0, min_y = 0, min_z = 0, mi_x = 255, mi_y = 255, mi_z = 255, ma_x = 0, ma_y = 0, ma_z = 0, x, y, z;
    int psiz = 256, siz, tr[256];

    /* detect dimensions and get palette */
    for(s = data + 20; s < e; s++) {
        if(!memcmp(s, "RGBA", 4)) {
            pal = (uint32_t*)(s + 8); psiz = *((uint32_t*)(s + 4)) / 4;
            /* use the children size field as the first (empty) color, and we don't have to mess with palette indices */
            pal[0] = 0;
        } else
        if(!memcmp(s, "XYZI", 4)) {
            siz = (*((uint32_t*)(s + 4)) / 4) - 1;
            s += 16;
            for(i = 0; i < siz; i++, s += 4) {
                if(s[0] < mi_x) mi_x = s[0];
                if(s[0] > ma_x) ma_x = s[0];
                if(s[1] < mi_z) mi_z = s[1];
                if(s[1] > ma_z) ma_z = s[1];
                if(s[2] < mi_y) mi_y = s[2];
                if(s[2] > ma_y) ma_y = s[2];
            }
            s--;
            continue;
        }
        s += *((uint32_t*)(s + 4)) + 11;
    }
    mts_x = ma_x - mi_x + 1;
    mts_y = ma_y - mi_y + 1;
    mts_z = ma_z - mi_z + 1;
    if(!pal || !psiz || mts_x < 1 || mts_y < 1 || mts_z < 1) { mts_y = mts_z = mts_x = 0; return; }
    blocks[0].numref = mts_y * mts_z * mts_x;
    min_x = 127 - mts_x / 2;
    min_y = 127 - mts_y / 2;
    min_z = 127 + mts_z / 2;
    gndlayer = min_y;

    /* get palette index translation */
    memset(tr, 0, sizeof(tr));
    for(i = 0; i < psiz; i++)
        tr[i] = color2blockid(pal[i]);

    /* read in the layers (possibly from multiple chunks) */
    for(s = data + 20; s < e; s++) {
        if(!memcmp(s, "XYZI", 4)) {
            siz = (*((uint32_t*)(s + 4)) / 4) - 1;
            s += 16;
            for(i = 0; i < siz; i++, s += 4) {
                x = s[0] - mi_x;
                z = s[1] - mi_z;
                y = s[2] - mi_y;
                nodes[min_y+y][min_z-z][x+min_x].param0 = tr[s[3]];
                if(nodes[min_y+y][min_z-z][x+min_x].param0) {
                    layerprob[min_y+y] = 127;
                    nodes[min_y+y][min_z-z][x+min_x].param1 = 127;
                    blocks[nodes[min_y+y][min_z-z][x+min_x].param0].numref++;
                    blocks[0].numref--;
                }
            }
            s--;
            continue;
        }
        s += *((uint32_t*)(s + 4)) + 11;
    }
    status = lang[LOADED];
}

/**
 * Save a Magicavoxel VOX file (this is an exception to support bidirectional workflows)
 */
int vox_save()
{
    char outfile[MAXPATHLEN], *c;
    unsigned short *tr, *tr2;
    unsigned char hdr[12 + 1024];
    int i, j, n = 0, x, y, z;
    FILE *f;

    status = lang[ERR_SAVE];
    tr = (unsigned short*)malloc(numblocks * sizeof(unsigned short));
    if(!tr) error(lang[ERR_MEM]);
    memset(tr, 0, numblocks * sizeof(unsigned short));
    tr2 = (unsigned short*)malloc(numblocks * sizeof(unsigned short));
    if(!tr2) error(lang[ERR_MEM]);
    memset(tr2, 0, numblocks * sizeof(unsigned short));

    j = mts_getbounds(1, tr, tr2);
    if(mix > max) { free(tr); free(tr2); return 1; }

    for(y = miy; y <= may; y++)
        for(z = miz; z <= maz; z++)
            for(x = mix; x <= max; x++)
                if(nodes[y][z][x].param0) n++;

    /* get filename, buffer and open file  */
    strcpy(outfile, mtsfile);
    c = strrchr(outfile, '.');
    if(!c) c = &outfile[strlen(outfile)];
    strcpy(c, ".vox");
    f = fopen(outfile,"wb");
    if(!f) return 1;

    /* write header */
    memcpy(hdr, "VOX ", 4);
    *((uint32_t*)(hdr+4)) = 150;
    memcpy(hdr + 8, "MAIN", 4);
    *((uint32_t*)(hdr+12)) = 0;
    *((uint32_t*)(hdr+16)) = 40 + n*4 + 1036;
    memcpy(hdr + 20, "SIZE", 4);
    *((uint32_t*)(hdr+24)) = 12;
    *((uint32_t*)(hdr+28)) = 0;
    *((uint32_t*)(hdr+32)) = max-mix+1;
    *((uint32_t*)(hdr+36)) = may-miy+1;
    *((uint32_t*)(hdr+40)) = maz-miz+1;
    memcpy(hdr + 44, "XYZI", 4);
    *((uint32_t*)(hdr+48)) = n*4 + 4;
    *((uint32_t*)(hdr+52)) = 0;
    *((uint32_t*)(hdr+56)) = n;
    fwrite(hdr, 60, 1, f);

    /* write data */
    for(y = miy; y <= may; y++)
        for(z = miz; z <= maz; z++)
            for(x = mix; x <= max; x++)
                if(nodes[y][z][x].param0) {
                    hdr[0] = x - mix;
                    hdr[1] = maz - z;
                    hdr[2] = y - miy;
                    hdr[3] = tr2[nodes[y][z][x].param0];
                    fwrite(hdr, 4, 1, f);
                }

    /* write palette */
    memset(hdr, 0, sizeof(hdr));
    memcpy(hdr, "RGBA", 4);
    for(i = 0; i < j; i++)
        memcpy(hdr + 8 + i * 4, &blocks[tr[i]].color, 4);
    *((uint32_t*)(hdr+4)) = 1024;
    *((uint32_t*)(hdr+8)) = 0;
    fwrite(hdr, 12+1024, 1, f);

    status = lang[SAVED];
    fclose(f);
    free(tr);
    free(tr2);
    return 0;
}

/**
 * Load a Qubicle QB file
 */
void _qb_set(int cf, int x, int y, int z, unsigned char *s)
{
    int i;

    if(y < 0 || y > 255 || z < 0 || z > 255 || x < 0 || x > 255) return;
    i = color2blockid(!s[3] ? 0 : 0xFF000000 | (cf ?
        ((uint32_t)s[0] << 16) | ((uint32_t)s[1] << 8) | (uint32_t)s[2] : *((uint32_t*)s)));
    if(i) {
        nodes[y][z][x].param0 = i;
        layerprob[y] = 127;
        nodes[y][z][x].param1 = 127;
        blocks[nodes[y][z][x].param0].numref++;
        blocks[0].numref--;
    }
}
void qb_load(unsigned char *data, unsigned int size)
{
    int cf = data[4], za = data[8], co = data[12], mc = *((int*)(data+20));
    int i, j, l, min_x = 0, min_y = 0, min_z = 0, mi_x = 65536, mi_y = 65536, mi_z = 65536, ma_x = 0, ma_y = 0, ma_z = 0, x, y, z;
    int sx, sy, sz, px, py, pz;
    unsigned char *s, *e = data + size;
    uint32_t c;

    /* detect dimensions */
    for(s = data + 24, i = 0; i < mc && s < e; i++) {
        s += s[0] + 1;
        sx = *((int*)(s + 0)); sz = *((int*)(s + 4)); sy = *((int*)(s + 8));
        px = *((int*)(s + 12)); pz = *((int*)(s + 16)); py = *((int*)(s + 20));
        if(px < mi_x) { mi_x = px; } if(px+sx > ma_x) { ma_x = px+sx; }
        if(py < mi_y) { mi_y = py; } if(py+sy > ma_y) { ma_y = py+sy; }
        if(pz < mi_z) { mi_z = pz; } if(pz+sz > ma_z) { ma_z = pz+sz; }
        s += 24;
        if(co) {
            for(y = 0; y < sy; y++) {
                while(1) {
                    c = *((uint32_t*)s); s += 4;
                    if(c == 6 || c == 0x60000) break;
                    if(c == 2 || c == 0x20000) s += 8;
                }
            }
        } else
            s += sx * sy * sz * 4;
    }
    mts_x = ma_x - mi_x;
    mts_y = ma_y - mi_y;
    mts_z = ma_z - mi_z;
    if(mts_y > 255) mts_y = 255;
    if(mts_z > 255) mts_z = 255;
    if(mts_x > 255) mts_x = 255;
    if(mts_x < 1 || mts_y < 1 || mts_z < 1) { mts_y = mts_z = mts_x = 0; return; }
    blocks[0].numref = mts_y * mts_z * mts_x;
    min_x = 127 - mts_x / 2;
    min_y = 127 - mts_y / 2;
    min_z = 127 - mts_z / 2;
    gndlayer = min_y;

    /* read in the layers */
    for(s = data + 24, i = 0; i < mc && s < e; i++) {
        s += s[0] + 1;
        sx = *((int*)(s + 0)); sz = *((int*)(s + 4)); sy = *((int*)(s + 8));
        px = *((int*)(s + 12)) - mi_x + min_x; pz = *((int*)(s + 16)) - mi_z + min_z; py = *((int*)(s + 20)) - mi_y + min_y;
        s += 24;
        if(co) {
            for(y = 0; y < sy; y++) {
                for(i = 0; 1;) {
                    c = *((uint32_t*)s); s += 4;
                    if(c == 6 || c == 0x60000) break;
                    if(c == 2 || c == 0x20000) { l = *((int32_t*)s); s += 4; c = *((uint32_t*)s); s += 4; } else l = 1;
                    for(j = 0; j < l; j++, i++) {
                        x = i % sx; z = i / sx;
                        _qb_set(cf, px+x, pz + (za ? mts_z-z : z), py+y, (unsigned char*)&c);
                    }
                }
            }
        } else {
            for(y = 0; y < sy; y++)
                for(z = 0; z < sz; z++)
                    for(x = 0; x < sx; x++, s += 4)
                        _qb_set(cf, px+x, pz + (za ? mts_z-z : z), py+y, s);
        }
    }
    status = lang[LOADED];
}

/**
 * Load a WorldEdit .we file
 */
void we_load(unsigned char *data, unsigned int size)
{
    unsigned char *s, *d, *e = data + size;
    unsigned int idx;
    int j, k, l, min_x = 0, min_y = 0, min_z = 0, mi_x = 65536, mi_y = 65536, mi_z = 65536, ma_x = 0, ma_y = 0, ma_z = 0;
    int x, y, z, b, p0, p1, p2, *palref;

    /* detect dimensions */
    while(data < e && *data != '{') data++;
    if(*data != '{') return;
    data++;
    for(s = data, b = 0; b >= 0 && s < e; s++) {
        if(b == 1 && *s == '[') {
            if(!memcmp(s, "[\"x\"]", 5)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ')) s++;
                x = atoi((char*)s);
                if(x < mi_x) mi_x = x;
                if(x > ma_x) ma_x = x;
            } else
            if(!memcmp(s, "[\"y\"]", 5)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ')) s++;
                y = atoi((char*)s);
                if(y < mi_y) mi_y = y;
                if(y > ma_y) ma_y = y;
            } else
            if(!memcmp(s, "[\"z\"]", 5)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ')) s++;
                z = atoi((char*)s);
                if(z < mi_z) mi_z = z;
                if(z > ma_z) ma_z = z;
            }
        }
        if(*s == '{') b++; else
        if(*s == '}') b--;
    }
    mts_x = ma_x - mi_x + 1;
    mts_y = ma_y - mi_y + 1;
    mts_z = ma_z - mi_z + 1;
    if(mts_y > 255) mts_y = 255;
    if(mts_z > 255) mts_z = 255;
    if(mts_x > 255) mts_x = 255;
    if(mts_x < 1 || mts_y < 1 || mts_z < 1) { mts_y = mts_z = mts_x = 0; return; }
    blocks[0].numref = mts_y * mts_z * mts_x;
    min_x = 127 - mts_x / 2;
    min_y = 127 - mts_y / 2;
    min_z = 127 - mts_z / 2;
    gndlayer = min_y;

    palref = (int*)malloc(numpalettes * sizeof(int));
    if(!palref) error(lang[ERR_MEM]);

    p0 = p2 = 0; p1 = 127; x = y = z = -256;
    for(s = data, b = 0; b >= 0 && s < e; s++) {
        if(b == 1 && *s == '[') {
            if(!memcmp(s, "[\"x\"]", 5)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ')) s++;
                x = atoi((char*)s);
            } else
            if(!memcmp(s, "[\"y\"]", 5)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ')) s++;
                y = atoi((char*)s);
            } else
            if(!memcmp(s, "[\"z\"]", 5)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ')) s++;
                z = atoi((char*)s);
            } else
            if(!memcmp(s, "[\"param1\"]", 10)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ')) s++;
                p1 = atoi((char*)s);
            } else
            if(!memcmp(s, "[\"param2\"]", 10)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ')) s++;
                p2 = atoi((char*)s);
            } else
            if(!memcmp(s, "[\"name\"]", 8)) {
                while(s < e && *s != '=') s++;
                while(s < e && (*s == '=' || *s == ' ' || *s == '\"')) s++;
                for(d = s, j = 0; d < e && *d && *d != '\"'; d++, j++);
                p0 = numblocks;
                for(k = 0; k < numblocks; k++)
                    for(l = 0; l < numpalettes + 3; l++)
                        if((!blocks[k].blocknames && (int)strlen(blocks[k].name) == j &&
                            !memcmp(blocks[k].name, s, j)) || (blocks[k].blocknames && blocks[k].blocknames[l] &&
                            (int)strlen(blocks[k].blocknames[l]) == j && !memcmp(blocks[k].blocknames[l], s, j))) {
                                if(k)
                                    switch(l) {
                                        case 0: break;
                                        case 1: blocks[k].dobiome = 1; savebiome = 1; break;
                                        case 2: if(!memcmp(s, "mapgen", 6)) { savemapgen = 1; } break;
                                        default: palref[l - 3]++; break;
                                    }
                                p0 = k;
                                break;
                            }
                if(p0 == numblocks) {
                    l = s[j]; s[j] = 0;
                    fprintf(stderr, "mtsedit: %s: %s %d '%s'\r\n", mtsfile, lang[ERR_NODE], p0, s);
                    s[j] = l;
                    idx = numblocks++;
                    blocks = (mtsblock_t*)realloc(blocks, numblocks * sizeof(mtsblock_t));
                    if(!blocks) error(lang[ERR_MEM]);
                    memset(&blocks[idx], 0, sizeof(mtsblock_t));
                    blocks[idx].name = (char*)malloc(j + 1);
                    if(!blocks[idx].name) error(lang[ERR_MEM]);
                    memcpy(blocks[idx].name, s, j);
                    blocks[idx].name[j] = 0;
                }
                s = d;
            }
        }
        if(*s == '{') b++; else
        if(*s == '}') {
            b--;
            if(!b) {
                if(p0 && x != -256 && y != -256 && z != -256) {
                    layerprob[min_y+y] = 127;
                    nodes[min_y+y][mts_z+min_z-z][x+min_x].param0 = p0;
                    nodes[min_y+y][mts_z+min_z-z][x+min_x].param1 = p1;
                    nodes[min_y+y][mts_z+min_z-z][x+min_x].param2 = p2;
                    blocks[p0].numref++;
                    blocks[0].numref--;
                }
                p0 = p2 = 0; p1 = 127; x = y = z = -256;
            }
        }
    }
    /* pick the node palette which has the most node reference */
    for(j = savepal = 0; j < numpalettes; j++)
        if(palref[j] > palref[savepal]) savepal = j;
    free(palref);
    status = lang[LOADED];
}
