mtsedit/src/schemimp.c

979 lines
40 KiB
C

/*
* 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; 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--; }
}
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];
}