760 lines
27 KiB
C
760 lines
27 KiB
C
/*
|
|
* mtsedit/mts.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 MTS file format importer / exporter
|
|
*
|
|
*/
|
|
|
|
#include "main.h"
|
|
|
|
char mtsfile[MAXPATHLEN];
|
|
/* block type mapping palettes */
|
|
int numpalettes = 0, lenpalettes = 0;
|
|
char **palettes = NULL;
|
|
/* blocks types */
|
|
int numblocks = 0;
|
|
mtsblock_t *blocks = NULL;
|
|
|
|
/* the actual MTS in memory */
|
|
int mts_x = 0, mts_y = 0, mts_z = 0;
|
|
int currlayer = 127, gndlayer = 0;
|
|
char layerprob[256];
|
|
node_t nodes[256][256][256];
|
|
|
|
/* generated properties */
|
|
int mix = 255, max = 0, miy = 255, may = 0, miz = 255, maz = 0, bound_valid = 0;
|
|
|
|
/**
|
|
* Get schematic bounding cube and node id translation tables
|
|
*/
|
|
int mts_getbounds(int sanitize, unsigned short *tr, unsigned short *tr2)
|
|
{
|
|
int x, y, z, j;
|
|
|
|
if(!sanitize && !tr && bound_valid) return 0;
|
|
|
|
/* get boundind box and block ID translation tables, sanitize probability values */
|
|
j = 1; mix = 255; max = 0; miy = 255; may = 0; miz = 255; maz = 0;
|
|
for(y = 0; y < 256; y++) {
|
|
for(z = 0; z < 256; z++)
|
|
for(x = 0; x < 256; x++) {
|
|
if(nodes[y][z][x].param0) {
|
|
if(tr && tr2 && !tr2[nodes[y][z][x].param0]) {
|
|
tr2[nodes[y][z][x].param0] = j;
|
|
tr[j++] = nodes[y][z][x].param0;
|
|
}
|
|
if(x < mix) mix = x;
|
|
if(x > max) max = x;
|
|
if(y < miy) miy = y;
|
|
if(y > may) may = y;
|
|
if(z < miz) miz = z;
|
|
if(z > maz) maz = z;
|
|
if(sanitize && !(nodes[y][z][x].param1 & 0x7F)) nodes[y][z][x].param1 |= 127;
|
|
} else
|
|
if(sanitize) nodes[y][z][x].param1 = 0;
|
|
}
|
|
}
|
|
if(sanitize)
|
|
for(y = miy; y <= may; y++)
|
|
if(!layerprob[y]) layerprob[y] = 127;
|
|
bound_valid = 1;
|
|
return j;
|
|
}
|
|
|
|
/**
|
|
* Load an MTS file
|
|
*/
|
|
void mts_load(unsigned char *data, unsigned int size)
|
|
{
|
|
unsigned char *buff = NULL, *un = NULL, *b;
|
|
unsigned int idx;
|
|
int i, j, k, l, n, x, y, z, min_x = 0, min_y = 0, min_z = 0, *palref;
|
|
unsigned short *tr = NULL;
|
|
|
|
if(!data[4] && (data[5] == 3 || data[5] == 4) && !data[6] && data[7] && !data[8] && data[9] && !data[10] && data[11]) {
|
|
mts_x = data[7]; mts_y = data[9]; mts_z = data[11];
|
|
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;
|
|
buff = data + 12;
|
|
/* guess if we have layer probability map or not for version 3 */
|
|
if(buff[0] > 3 || data[5] == 4) {
|
|
memcpy(layerprob + min_y, buff, mts_y);
|
|
buff += mts_y;
|
|
}
|
|
n = (buff[0] << 8) | buff[1];
|
|
if(n > 0 && n < 1024) {
|
|
buff += 2;
|
|
tr = (unsigned short*)malloc(n * sizeof(unsigned short));
|
|
if(!tr) error(lang[ERR_MEM]);
|
|
palref = (int*)malloc(numpalettes * sizeof(int));
|
|
if(!palref) error(lang[ERR_MEM]);
|
|
memset(palref, 0, numpalettes * sizeof(int));
|
|
for(i = 0; i < n; i++) {
|
|
tr[i] = numblocks;
|
|
j = buff[1];
|
|
buff += 2;
|
|
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, buff, j)) || (blocks[k].blocknames && blocks[k].blocknames[l] &&
|
|
(int)strlen(blocks[k].blocknames[l]) == j && !memcmp(blocks[k].blocknames[l], buff, j))) {
|
|
if(k)
|
|
switch(l) {
|
|
case 0: break;
|
|
case 1: blocks[k].dobiome = 1; savebiome = 1; break;
|
|
case 2: if(!memcmp(buff, "mapgen", 6)) { savemapgen = 1; } break;
|
|
default: palref[l - 3]++; break;
|
|
}
|
|
tr[i] = k;
|
|
break;
|
|
}
|
|
if(tr[i] == numblocks) {
|
|
x = buff[j]; buff[j] = 0;
|
|
fprintf(stderr, "mtsedit: %s: %s %d '%s'\r\n", mtsfile, lang[ERR_NODE], i, buff);
|
|
buff[j] = x;
|
|
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, buff, j);
|
|
blocks[idx].name[j] = 0;
|
|
}
|
|
buff += j;
|
|
}
|
|
/* 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);
|
|
size -= (int)(buff - data);
|
|
un = (uint8_t*)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)buff, size, 4096, &i, 1);
|
|
if(un) { buff = un; size = (unsigned int)i; }
|
|
if(4 * mts_x * mts_y * mts_z <= (int)size) {
|
|
for(z = 0, b = buff; z < mts_z; z++)
|
|
for(y = 0; y < mts_y; y++)
|
|
for(x = 0; x < mts_x; x++, b += 2) {
|
|
nodes[y+min_y][mts_z+min_z-z][x+min_x].param0 = tr[(b[0] << 8) | b[1]];
|
|
blocks[nodes[y+min_y][mts_z+min_z-z][x+min_x].param0].numref++;
|
|
}
|
|
for(z = 0; z < mts_z; z++)
|
|
for(y = 0; y < mts_y; y++)
|
|
for(x = 0; x < mts_x; x++, b++)
|
|
nodes[y+min_y][mts_z+min_z-z][x+min_x].param1 = data[5] == 3 ? b[0] >> 1 : b[0];
|
|
for(z = 0; z < mts_z; z++)
|
|
for(y = 0; y < mts_y; y++)
|
|
for(x = 0; x < mts_x; x++, b++)
|
|
nodes[y+min_y][mts_z+min_z-z][x+min_x].param2 = b[0];
|
|
gndlayer = currlayer = min_y;
|
|
for(y = 0; y < mts_y; y++)
|
|
for(z = 0; z < mts_z; z++)
|
|
for(x = 0; x < mts_x; x++)
|
|
if(nodes[y+min_y][z+min_z][x+min_x].param2 == 0x20) {
|
|
gndlayer = y+min_y;
|
|
nodes[y+min_y][z+min_z][x+min_x].param2 = 0;
|
|
}
|
|
status = lang[LOADED];
|
|
} else mts_x = mts_y = mts_z = 0;
|
|
free(tr);
|
|
if(un) free(un);
|
|
} else mts_x = mts_y = mts_z = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save an MTS file
|
|
*/
|
|
int mts_save()
|
|
{
|
|
char outfile[MAXPATHLEN], *c;
|
|
unsigned short *tr, *tr2;
|
|
unsigned char *b, *b2, hdr[12];
|
|
int i, j, k, 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++) {
|
|
/* close windows and doors */
|
|
for(z = miz+1; z < maz; z++)
|
|
for(x = mix+1; x < max; x++)
|
|
if(!nodes[y][z][x].param0 && !nodes[y][z][x].param1 && (
|
|
(nodes[y][z-1][x].param1 && (nodes[y][z+1][x].param1 || (z<254 && nodes[y][z+2][x].param1))) ||
|
|
(nodes[y][z][x-1].param1 && (nodes[y][z][x+1].param1 || (x<254 && nodes[y][z][x+2].param1)))))
|
|
nodes[y][z][x].param1 = 127;
|
|
/* mark air in rooms temporarly */
|
|
for(z = miz+1; z < maz; z++)
|
|
for(x = mix+1; x < max; x++)
|
|
if(!nodes[y][z][x].param0 && !nodes[y][z][x].param1 &&
|
|
(nodes[y][z-1][x].param1 || nodes[y][z][x-1].param1))
|
|
nodes[y][z][x].param1 = 1;
|
|
/* clear rooms which are connected with the outside */
|
|
do {
|
|
k = 0;
|
|
for(z = miz+1; z < maz; z++)
|
|
for(x = mix+1; x < max; x++)
|
|
if(nodes[y][z][x].param1 == 1 &&
|
|
(!nodes[y][z-1][x].param1 || !nodes[y][z+1][x].param1 || !nodes[y][z][x-1].param1 ||
|
|
!nodes[y][z][x+1].param1)) {
|
|
nodes[y][z][x].param1 = 0;
|
|
k = 1;
|
|
}
|
|
} while(k);
|
|
/* make air in closed rooms permanent, also set the "force placement" flag for them */
|
|
for(z = miz+1; z < maz; z++)
|
|
for(x = mix+1; x < max; x++)
|
|
if(nodes[y][z][x].param1 == 1) nodes[y][z][x].param1 = 255;
|
|
/* mark ground level */
|
|
if(y == gndlayer)
|
|
for(z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++)
|
|
if(!nodes[y][z][x].param0) nodes[y][z][x].param2 = 0x20;
|
|
}
|
|
|
|
/* get filename, buffer and open file */
|
|
strcpy(outfile, mtsfile);
|
|
c = strrchr(outfile, '.');
|
|
if(!c) c = &outfile[strlen(outfile)];
|
|
strcpy(c, ".mts");
|
|
f = fopen(outfile,"wb");
|
|
if(!f) return 1;
|
|
b = (unsigned char*)malloc((max-mix+1) * (may-miy+1) * (maz-miz+1) * 4);
|
|
if(!b) error(lang[ERR_MEM]);
|
|
memset(b, 0, (max-mix+1) * (may-miy+1) * (maz-miz+1) * 4);
|
|
memset(hdr, 0, sizeof(hdr));
|
|
|
|
/* write header */
|
|
memcpy(hdr, "MTSM", 4);
|
|
hdr[5] = 4;
|
|
hdr[7] = max-mix+1;
|
|
hdr[9] = may-miy+1;
|
|
hdr[11] = maz-miz+1;
|
|
fwrite(hdr, 12, 1, f);
|
|
fwrite(layerprob + miy, may-miy+1, 1, f);
|
|
|
|
/* write Name-ID table using the selected node palette */
|
|
hdr[0] = 0; hdr[1] = j;
|
|
fwrite(hdr, 2, 1, f);
|
|
if(savepal < 0 || savepal >= numpalettes) savepal = 0;
|
|
for(i = 0; i < j; i++) {
|
|
c = !blocks[tr[i]].blocknames ? blocks[tr[i]].name : (savebiome && blocks[tr[i]].blocknames[1] &&
|
|
blocks[tr[i]].dobiome ? blocks[tr[i]].blocknames[1] : (savemapgen && blocks[tr[i]].blocknames[2] &&
|
|
blocks[tr[i]].blocknames[2][0] ? blocks[tr[i]].blocknames[2] : (blocks[tr[i]].blocknames[savepal+3] &&
|
|
blocks[tr[i]].blocknames[savepal+3][0] ? blocks[tr[i]].blocknames[savepal+3] : blocks[tr[i]].name)));
|
|
hdr[1] = strlen(c);
|
|
fwrite(hdr, 2, 1, f);
|
|
fwrite(c, hdr[1], 1, f);
|
|
}
|
|
|
|
/* construct param buffer */
|
|
for(z = miz, b2 = b; z <= maz; z++)
|
|
for(y = miy; y <= may; y++)
|
|
for(x = mix; x <= max; x++, b2 += 2) {
|
|
b2[0] = tr2[nodes[y][maz+miz-z][x].param0] >> 8;
|
|
b2[1] = tr2[nodes[y][maz+miz-z][x].param0] & 0xFF;
|
|
}
|
|
for(z = miz; z <= maz; z++)
|
|
for(y = miy; y <= may; y++)
|
|
for(x = mix; x <= max; x++, b2++)
|
|
*b2 = nodes[y][maz+miz-z][x].param1;
|
|
for(z = miz; z <= maz; z++)
|
|
for(y = miy; y <= may; y++)
|
|
for(x = mix; x <= max; x++, b2++)
|
|
*b2 = nodes[y][maz+miz-z][x].param2;
|
|
/* compress and save */
|
|
x = 1;
|
|
b2 = stbi_zlib_compress(b, (max-mix+1) * (may-miy+1) * (maz-miz+1) * 4, &i, 9);
|
|
if(b2) {
|
|
fwrite(b2, i, 1, f);
|
|
free(b2);
|
|
status = lang[SAVED];
|
|
x = 0;
|
|
}
|
|
fclose(f);
|
|
free(b);
|
|
free(tr);
|
|
free(tr2);
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* Save a PNG preview file
|
|
*/
|
|
int mts_view(int type)
|
|
{
|
|
int i, j, w, h, x, y, z, lh = 0;
|
|
int r = 0, k, n, l;
|
|
char pngfile[MAXPATHLEN], *c;
|
|
unsigned char *buff = NULL;
|
|
SDL_Surface *preview, *blk;
|
|
SDL_Rect src, dst;
|
|
FILE *f;
|
|
|
|
status = lang[ERR_PREVIEW];
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(mix > max) return 1;
|
|
bound_valid = 0;
|
|
|
|
/* calculate dimensions and create images */
|
|
n = (max - mix) + (maz - miz) + 1;
|
|
w = n * dx + 16;
|
|
lh = n * dz + 32 - dz;
|
|
l = 32 - 2*(_y0 + dz);
|
|
h = lh + (may - miy) * l;
|
|
blk = SDL_CreateRGBSurfaceFrom(NULL, 32, 32, 32, 32*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
|
|
preview = SDL_CreateRGBSurface(0, w, h, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
|
|
src.x = src.y = 0; src.w = src.h = dst.w = dst.h = 32;
|
|
n = ((max-mix)+(maz-miz))/2;
|
|
|
|
/* generate preview into surface */
|
|
for(y = miy; y <= may; y++)
|
|
for(z = miz; z <= maz; z++) {
|
|
if(type) r = ((z-miz+1)*(max-mix+1)*(y-miy)/(maz-miz+1))/(may-miy);
|
|
for(x = mix; x <= max - r; x++) {
|
|
if(nodes[y][z][x].param0) {
|
|
dst.x = dx * ((x-mix) - (z-miz)) + (maz-miz) * dx;
|
|
dst.y = h - lh + dz * ((z-miz) + (x-mix)) - (y-miy) * l;
|
|
i = nodes[y][z][x].param0;
|
|
if(i && blocks[i].img) {
|
|
j = nodes[y][z][x].param2;
|
|
j %= (int)blocks[i].numpar2;
|
|
memcpy(tmpblk, blocks[i].img + j * 32 * 32 * 4, 32 * 32 * 4);
|
|
for(j = 0; j < 32 * 32 * 4; j += 4) {
|
|
k = (int)(tmpblk[j+0]) * ((x-mix)+(z-miz)) / n;
|
|
tmpblk[j+0] = k > 255 ? 255 : (k > tmpblk[j+0] / 2 ? k : tmpblk[j+0] / 2);
|
|
k = (int)(tmpblk[j+1]) * ((x-mix)+(z-miz)) / n;
|
|
tmpblk[j+1] = k > 255 ? 255 : (k > tmpblk[j+1] / 2 ? k : tmpblk[j+1] / 2);
|
|
k = (int)(tmpblk[j+2]) * ((x-mix)+(z-miz)) / n;
|
|
tmpblk[j+2] = k > 255 ? 255 : (k > tmpblk[j+2] / 2 ? k : tmpblk[j+2] / 2);
|
|
}
|
|
blk->pixels = tmpblk;
|
|
SDL_BlitSurface(blk, &src, preview, &dst);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SDL_FreeSurface(blk);
|
|
|
|
/* crop the result */
|
|
buff = preview->pixels;
|
|
for(i = miy = 0; i < preview->pitch * h; i += 4)
|
|
if(buff[i + 3]) { miy = i / preview->pitch; break; }
|
|
for(i = may = preview->pitch * preview->h - 4; i ; i -= 4)
|
|
if(buff[i + 3]) { may = i / preview->pitch; break; }
|
|
for(i = 0, mix = -1; i < w/2 && mix == -1; i++)
|
|
for(j = 0; j < h; j++)
|
|
if(buff[j * preview->pitch + i * 4 + 3]) { mix = i; break; }
|
|
for(i = w-1, max = -1; i > w/2 && max == -1; i--)
|
|
for(j = 0; j < h; j++)
|
|
if(buff[j * preview->pitch + i * 4 + 3]) { max = i; break; }
|
|
if(mix >= 0 && max > 0 && may > miy) {
|
|
blk = SDL_CreateRGBSurface(0, max-mix+1, may-miy+1, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
|
|
src.x = mix; src.y = miy;
|
|
dst.x = dst.y = 0;
|
|
src.w = dst.w = blk->w;
|
|
src.h = dst.h = blk->h;
|
|
SDL_BlitSurface(preview, &src, blk, &dst);
|
|
SDL_FreeSurface(preview);
|
|
|
|
/* write result to PNG file */
|
|
buff = (unsigned char *)stbi_write_png_to_mem(blk->pixels, blk->pitch, blk->w, blk->h, 4, &i);
|
|
SDL_FreeSurface(blk);
|
|
if(buff) {
|
|
strcpy(pngfile, mtsfile);
|
|
c = strrchr(pngfile, '.');
|
|
if(!c) c = &pngfile[strlen(pngfile)];
|
|
strcpy(c, ".png");
|
|
f = fopen(pngfile,"wb");
|
|
if(f) {
|
|
fwrite(buff, i, 1, f);
|
|
fclose(f);
|
|
status = lang[PREVIEWSAVED];
|
|
fprintf(stderr, "mtsedit: %s: %s\r\n", pngfile, status);
|
|
}
|
|
free(buff);
|
|
}
|
|
return 0;
|
|
}
|
|
SDL_FreeSurface(preview);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Export a PNG blueprint file
|
|
*/
|
|
int mts_blueprint(int type)
|
|
{
|
|
int oldsmax = strmaxw, i, j, k, l, bw, w, h, x, y, z;
|
|
char pngfile[MAXPATHLEN], *s, *e, *c;
|
|
unsigned char *buff = NULL;
|
|
uint32_t *dst;
|
|
SDL_Surface *oldscr = screen;
|
|
SDL_Rect rect;
|
|
FILE *f;
|
|
|
|
status = lang[ERR_PREVIEW];
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(mix > max) return 1;
|
|
bound_valid = 0;
|
|
|
|
strcpy(pngfile, mtsfile);
|
|
s = strrchr(pngfile, DIRSEP);
|
|
if(!s) s = pngfile; else s++;
|
|
e = strrchr(pngfile, '.');
|
|
if(!e) e = &pngfile[strlen(pngfile)];
|
|
|
|
/* calculate dimensions and create image */
|
|
bw = maz - miz;
|
|
h = (may - miy + 1) * (max - mix + 2) + 3;
|
|
if(!type) {
|
|
for(i = 1, j = l = 0; i < numblocks; i++)
|
|
if(blocks[i].numref) {
|
|
l += font->height;
|
|
k = mbstrlen(blocks[i].name);
|
|
if(k > j) j = k;
|
|
}
|
|
w = bw + 8 + (4 + j) * (font->width + 1);
|
|
if(l > h) h = l;
|
|
h += font->height + 5;
|
|
} else
|
|
w = bw + 3;
|
|
screen = SDL_CreateRGBSurface(0, w, h, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
|
|
memset(screen->pixels, 0, w * h * 4);
|
|
dst = (uint32_t*)screen->pixels;
|
|
if(!type) {
|
|
for(i = 0; i < w * h; i++) dst[i] = theme[THEME_BP_BG];
|
|
|
|
/* print title */
|
|
for(c = s; c < e; c++)
|
|
if(*c == '_') *c = ' ';
|
|
*e = 0; strmaxw = screen->w - 1;
|
|
i = mbstrlen(s) * (font->width+1);
|
|
sdlprint((screen->w - (i < screen->w ? i : screen->w)) / 2, 0, THEME_BP_FG, THEME_BP_BG, s);
|
|
|
|
/* print legend */
|
|
rect.x = bw + font->height; rect.y = font->height + 4; rect.w = rect.h = font->height - 2;
|
|
for(i = 1; i < numblocks; i++)
|
|
if(blocks[i].numref) {
|
|
SDL_FillRect(screen, &rect, blocks[i].color);
|
|
sdlprint(rect.x + 4 + font->height, rect.y, THEME_BP_FG, THEME_BP_BG, blocks[i].name);
|
|
rect.y += font->height;
|
|
}
|
|
i = w * (font->height + 5);
|
|
} else
|
|
i = w;
|
|
/* generate blueprint into surface */
|
|
for(y = may; y >= miy; y--, i += w) {
|
|
dst[i] = (y == gndlayer ? 0xFF0000FF : 0xFF000000);
|
|
for(x = mix; x <= max; x++) {
|
|
i++;
|
|
for(z = maz; z >= miz; z--, i++)
|
|
dst[i] = (nodes[y][z][x].param0 ? blocks[nodes[y][z][x].param0].color : 0);
|
|
if(y == gndlayer)
|
|
dst[i] = 0xFF0000FF;
|
|
i += w - bw - 2;
|
|
}
|
|
}
|
|
|
|
/* write result to PNG file */
|
|
buff = (unsigned char *)stbi_write_png_to_mem(screen->pixels, screen->pitch, screen->w, screen->h, 4, &i);
|
|
SDL_FreeSurface(screen);
|
|
screen = oldscr;
|
|
strmaxw = oldsmax;
|
|
if(buff) {
|
|
strcpy(pngfile, mtsfile);
|
|
strcpy(e, "_bp.png");
|
|
f = fopen(pngfile,"wb");
|
|
if(f) {
|
|
fwrite(buff, i, 1, f);
|
|
fclose(f);
|
|
status = lang[BPRINTSAVED];
|
|
fprintf(stderr, "mtsedit: %s: %s\r\n", pngfile, status);
|
|
}
|
|
free(buff);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Dump structure
|
|
*/
|
|
int mts_dump()
|
|
{
|
|
unsigned short *tr, *tr2;
|
|
int i, j, x, y, z;
|
|
|
|
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(0, tr, tr2);
|
|
if(mix > max) { free(tr); free(tr2); return 1; }
|
|
|
|
printf("-------------------------------- %s --------------------------------\r\n", mtsfile);
|
|
printf("Map (x: %d y: %d z: %d):\r\n", max-mix+1, may-miy+1, maz-miz+1);
|
|
if(savepal < 0 || savepal >= numpalettes) savepal = 0;
|
|
for(i = 0; i < j; i++)
|
|
printf("%4x: %s\r\n", i, blocks[tr[i]].blocknames && blocks[tr[i]].blocknames[savepal+3] ?
|
|
blocks[tr[i]].blocknames[savepal+3] : blocks[tr[i]].name);
|
|
|
|
for(y = miy; y <= may; y++) {
|
|
printf("\r\nLayer %d (probability %d%s):\r\n", y - miy, layerprob[y], y == gndlayer ? ", Ground level" : "");
|
|
for(z = miz; z <= maz; z++) {
|
|
if(j < 15) printf(" ");
|
|
for(x = mix; x <= max; x++) {
|
|
if(nodes[y][z][x].param0) {
|
|
if(j < 16) printf("%x", tr2[nodes[y][z][x].param0]);
|
|
else printf(" %02x", tr2[nodes[y][z][x].param0]);
|
|
} else printf(j < 16 ? "." : " ..");
|
|
}
|
|
printf(" ");
|
|
for(x = mix; x <= max; x++)
|
|
printf(" %02x", nodes[y][z][x].param1);
|
|
printf(" ");
|
|
for(x = mix; x <= max; x++)
|
|
printf(" %02x", y == gndlayer && !nodes[y][z][x].param0 ? 32 : nodes[y][z][x].param2);
|
|
printf("\r\n");
|
|
}
|
|
}
|
|
|
|
free(tr);
|
|
free(tr2);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Change layer probability
|
|
*/
|
|
void mts_layerprob(int diff)
|
|
{
|
|
if(diff < 0 && (int)layerprob[currlayer] > 0) layerprob[currlayer]--;
|
|
if(diff > 0 && (int)layerprob[currlayer] < 127) layerprob[currlayer]++;
|
|
}
|
|
|
|
/**
|
|
* Rotate the entire MTS structure
|
|
*/
|
|
void mts_rotate(int ccw)
|
|
{
|
|
int nx, nz, x, y, z, o;
|
|
node_t layer[256][256];
|
|
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(mix > max) return;
|
|
|
|
for(y = miy; y <= may; y++) {
|
|
memcpy(layer, nodes[y], sizeof(node_t)*256*256);
|
|
memset(nodes[y], 0, sizeof(node_t)*256*256);
|
|
for(z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++) {
|
|
if(ccw) { nz = 255-x; nx = z; } else { nz = x; nx = 255-z; }
|
|
memcpy(&nodes[y][nz][nx], &layer[z][x], sizeof(node_t));
|
|
if(nodes[y][nz][nx].param0 && blocks[nodes[y][nz][nx].param0].rotate) {
|
|
o = nodes[y][nz][nx].param2;
|
|
if(ccw)
|
|
nodes[y][nz][nx].param2 = (nodes[y][nz][nx].param2 & ~3) | (((nodes[y][nz][nx].param2 & 3)-1) & 3);
|
|
else
|
|
nodes[y][nz][nx].param2 = (nodes[y][nz][nx].param2 & ~3) | (((nodes[y][nz][nx].param2 & 3)+1) & 3);
|
|
if(o != nodes[y][nz][nx].param2) {
|
|
hist_prepare(HIST_NODE, 0);
|
|
hist_add(y, nz, nx, nodes[y][nz][nx].param0, o, nodes[y][nz][nx].param0, nodes[y][nz][nx].param2);
|
|
hist_commit();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
bound_valid = 0;
|
|
mts_getbounds(0, NULL, NULL);
|
|
}
|
|
|
|
/**
|
|
* Flip the entire MTS structure along the Z axis
|
|
*/
|
|
void mts_flip()
|
|
{
|
|
int x, y, z, o;
|
|
node_t layer[256][256];
|
|
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(mix > max) return;
|
|
|
|
for(y = miy; y <= may; y++) {
|
|
memcpy(layer, nodes[y], sizeof(node_t)*256*256);
|
|
for(z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++) {
|
|
memcpy(&nodes[y][z][x], &layer[maz-z+miz][x], sizeof(node_t));
|
|
if(nodes[y][z][x].param0 && blocks[nodes[y][z][x].param0].rotate) {
|
|
o = nodes[y][z][x].param2;
|
|
nodes[y][z][x].param2 = (nodes[y][z][x].param2 & ~3) | (((nodes[y][z][x].param2 & 3)+2) & 3);
|
|
if(o != nodes[y][z][x].param2) {
|
|
hist_prepare(HIST_NODE, 0);
|
|
hist_add(y, z, x, nodes[y][z][x].param0, o, nodes[y][z][x].param0, nodes[y][z][x].param2);
|
|
hist_commit();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
bound_valid = 0;
|
|
mts_getbounds(0, NULL, NULL);
|
|
}
|
|
|
|
/**
|
|
* Insert an Y layer
|
|
*/
|
|
void mts_addy(int y, int copy)
|
|
{
|
|
int x, Y, z;
|
|
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(miy > may || y < miy || y > may) return;
|
|
|
|
for(Y = miy-1 < 0 ? 0 : miy-1; Y < y; Y++)
|
|
for(z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++)
|
|
memcpy(&nodes[Y][z][x], &nodes[Y+1][z][x], sizeof(node_t));
|
|
if(!copy)
|
|
for(z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++)
|
|
memset(&nodes[y][z][x], 0, sizeof(node_t));
|
|
if(miy) miy--;
|
|
}
|
|
|
|
/**
|
|
* Remove an Y layer
|
|
*/
|
|
void mts_dely(int y)
|
|
{
|
|
int x, Y, z;
|
|
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(miy > may || y < miy || y > may) return;
|
|
|
|
for(Y = y; Y >= miy; Y--)
|
|
for(z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++)
|
|
memcpy(&nodes[Y][z][x], &nodes[Y-1][z][x], sizeof(node_t));
|
|
for(z = miz; z <= maz; z++)
|
|
for(x = mix; x <= max; x++)
|
|
memset(&nodes[miy][z][x], 0, sizeof(node_t));
|
|
if(miy < y) miy++;
|
|
}
|
|
|
|
/**
|
|
* Insert an X layer
|
|
*/
|
|
void mts_addx(int x, int copy)
|
|
{
|
|
int X, y, z;
|
|
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(mix > max || x < mix || x > max) return;
|
|
|
|
for(y = miy; y <= may; y++)
|
|
for(z = miz; z <= maz; z++)
|
|
for(X = max+1 > 255 ? 255 : max+1; X > x ; X--)
|
|
memcpy(&nodes[y][z][X], &nodes[y][z][X-1], sizeof(node_t));
|
|
if(!copy)
|
|
for(y = miy; y <= may; y++)
|
|
for(z = miz; z <= maz; z++)
|
|
memset(&nodes[y][z][x], 0, sizeof(node_t));
|
|
if(max < 255) max++;
|
|
}
|
|
|
|
/**
|
|
* Remove an X layer
|
|
*/
|
|
void mts_delx(int x)
|
|
{
|
|
int X, y, z;
|
|
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(mix > max || x < mix || x > max) return;
|
|
|
|
for(y = miy; y <= may; y++)
|
|
for(z = miz; z <= maz; z++)
|
|
for(X = x; X <= max; X++)
|
|
memcpy(&nodes[y][z][X], &nodes[y][z][X+1], sizeof(node_t));
|
|
for(y = miy; y <= may; y++)
|
|
for(z = miz; z <= maz; z++)
|
|
memset(&nodes[y][z][max], 0, sizeof(node_t));
|
|
if(max > x) max--;
|
|
}
|
|
|
|
/**
|
|
* Insert a Z layer
|
|
*/
|
|
void mts_addz(int z, int copy)
|
|
{
|
|
int x, y, Z;
|
|
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(miz > maz || z < miz || z > maz) return;
|
|
|
|
for(y = miy; y <= may; y++)
|
|
for(Z = miz-1 < 0 ? 0 : miz-1; Z < z; Z++)
|
|
for(x = mix; x <= max; x++)
|
|
memcpy(&nodes[y][Z][x], &nodes[y][Z+1][x], sizeof(node_t));
|
|
if(!copy)
|
|
for(y = miy; y <= may; y++)
|
|
for(x = mix; x <= max; x++)
|
|
memset(&nodes[y][z][x], 0, sizeof(node_t));
|
|
if(miz) miz--;
|
|
}
|
|
|
|
/**
|
|
* Remove a Z layer
|
|
*/
|
|
void mts_delz(int z)
|
|
{
|
|
int x, y, Z;
|
|
|
|
mts_getbounds(0, NULL, NULL);
|
|
if(miz > maz || z < miz || z > maz) return;
|
|
|
|
for(y = miy; y <= may; y++)
|
|
for(Z = z; Z >= miz; Z--)
|
|
for(x = mix; x <= max; x++)
|
|
memcpy(&nodes[y][Z][x], &nodes[y][Z-1][x], sizeof(node_t));
|
|
for(y = miy; y <= may; y++)
|
|
for(x = mix; x <= max; x++)
|
|
memset(&nodes[y][miz][x], 0, sizeof(node_t));
|
|
if(miz < z) miz++;
|
|
}
|