mtsedit/src/util.c

1400 lines
52 KiB
C

/*
* mtsedit/util.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 Utilities
*
*/
#include "main.h"
#include "bzip2.h"
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#define TMPSIZE 80
/* cube coordinates */
int x0,x1,x2, _y0,_y1,y2,y3,y4;
int verbose = 0;
int fncmp(const void *a, const void *b) { return strcmp(*((const char**)a), *((const char**)b)); }
/**
* Read a file into memory from the data directory
*/
unsigned char *readfile(char *file, unsigned int *size)
{
FILE *f;
unsigned char *ret = NULL;
*size = 0;
if(!path || !fn) return NULL;
strcpy(fn, file);
f = fopen(path, "rb");
if(f) {
fseek(f, 0L, SEEK_END);
*size = (unsigned int)ftell(f);
fseek(f, 0L, SEEK_SET);
ret = (unsigned char*)malloc(*size + 1);
if(ret) {
fread(ret, *size, 1, f);
ret[*size] = 0;
} else
*size = 0;
fclose(f);
}
return ret;
}
/**
* Read schematics from file
*/
void readschem()
{
FILE *f;
bz_stream bz;
unsigned char *data = NULL, *buff = NULL;
unsigned int size = 0;
int x, y, z;
status = NULL;
if(isdir(mtsfile)) return;
bound_valid = 0;
mts_x = mts_y = mts_z = 0;
memset(layerprob, 127, sizeof(layerprob));
memset(nodes, 0, sizeof(nodes));
for(x = 0; x < numblocks; x++) {
blocks[x].dobiome = 0;
blocks[x].numref = 0;
}
blocks[0].numref = 256 * 256 * 256;
savepal = savemapgen = savebiome = 0;
currlayer = gndlayer = cx = cz = 127;
status = lang[ERR_LOAD];
f = fopen(mtsfile, "rb");
if(f) {
fseek(f, 0L, SEEK_END);
size = (unsigned int)ftell(f);
fseek(f, 0L, SEEK_SET);
data = (unsigned char*)malloc(size);
if(!data) error(lang[ERR_MEM]);
fread(data, size, 1, f);
fclose(f);
status = lang[ERR_BADFILE];
/* if it's gzip compressed, uncompress it first */
if(data[0] == 0x1f && data[1] == 0x8b) {
/* skip over gzip header */
buff = data + 3;
x = *buff++; buff += 6;
if(x & 4) { y = *buff++; y += (*buff++ << 8); buff += y; }
if(x & 8) { while(*buff++ != 0); }
if(x & 16) { while(*buff++ != 0); }
if(x & 2) buff += 2;
size -= (int)(buff - data);
buff = (uint8_t*)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)buff, size, 4096, &x, 0);
if(buff) { free(data); data = buff; buff = NULL; size = (unsigned int)x; }
} else
/* if it's bzip2 compressed */
if(data[0] == 'B' && data[1] == 'Z' && data[2] == 'h') {
memset(&bz, 0, sizeof(bz_stream));
if(BZ2_bzDecompressInit(&bz, 0, 0) != BZ_OK) return;
bz.next_in = (char*)data;
bz.avail_in = size;
buff = NULL;
size = 0;
do {
bz.avail_out = 65536;
size += bz.avail_out;
bz.next_out -= (uintptr_t)buff;
buff = realloc(buff, size);
if(!buff) return;
bz.next_out += (uintptr_t)buff;
x = BZ2_bzDecompress(&bz);
} while(x == BZ_OK && !bz.avail_out);
if(x != BZ_STREAM_END) return;
if(buff) { free(data); data = buff; buff = NULL; size -= bz.avail_out; }
BZ2_bzDecompressEnd(&bz);
} else buff = data;
/* if it's an MTS file */
if(!memcmp(data, "MTSM", 4)) mts_load(data, size); else
/* Could be a Minecraft NBT */
if(!memcmp(data + 3, "Schematic", 9)) schem_load(data, size); else
/* Blueprint PNG */
if(!memcmp(data, "\x89PNG", 4)) bprint_load(data, size); else
/* Model 3D */
if(!memcmp(data, "3DMO", 4)) m3d_load(data, size); else
/* Tiled TMX XML */
if(!memcmp(data, "<?xml", 5)) tmx_load(data, size); else
/* Goxel GOX file */
if(!memcmp(data, "GOX ", 4)) gox_load(data, size); else
/* Magicavoxel VOX file */
if(!memcmp(data, "VOX ", 4)) vox_load(data, size); else
/* It is a shame, but Qubicle QB files has no real magic */
if(!memcmp(data, "\001\001\000\000", 4)) qb_load(data, size); else
/* WorldEdit Lua table */
if(!memcmp(data + 1, ":return", 7)) we_load(data, size);
/* make sure that all non-air blocks have their probability set */
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(!(nodes[y][z][x].param1 & 0x7F)) nodes[y][z][x].param1 |= 127;
}
currlayer = gndlayer;
}
if(data) free(data);
}
/**
* Common error handler
*/
void error(char *msg)
{
#ifdef __WIN32__
MessageBox(NULL, msg, "MTSEdit", MB_ICONEXCLAMATION | MB_OK);
#else
fprintf(stderr, "mtsedit: %s\r\n", msg);
#endif
exit(1);
}
/**
* CSV error
*/
void errorcsv(int line)
{
char msg[1024];
sprintf(msg, "blocks.csv(%d): %s", line, lang[ERR_CSV]);
error(msg);
}
/**
* IMGCSV error
*/
void errorimgcsv(int line)
{
char msg[1024];
sprintf(msg, "blockimgs.csv(%d): %s", line, lang[ERR_IMGCSV]);
}
/**
* UTF-8 strlen
*/
int mbstrlen(char *s)
{
int r = 0;
for(; *s; s++, r++) {
if((*s & 128) != 0) {
if(!(*s & 32)) s++; else
if(!(*s & 16)) s += 2; else
if(!(*s & 8)) *s += 3;
}
}
return r;
}
/**
* Returns true if argument is a directory
*/
int isdir(char *path)
{
DIR *dir;
dir = opendir(path);
if(dir) { closedir(dir); return 1; }
return 0;
}
/**
* Get contents of a directory
*/
int listdir(char *path, char ***files, int type)
{
DIR *dir;
struct dirent *de;
int i, j, numfiles = 0;
char tmp[MAXPATHLEN], *fn, c;
strcpy(tmp, path);
if(tmp[strlen(tmp)-1] == DIRSEP) tmp[strlen(tmp)-1] = 0;
dir = opendir(path);
if(!dir) {
fn = strrchr(tmp, DIRSEP);
if(!fn) fn = tmp; else fn++;
*fn = 0;
dir = opendir(tmp);
} else
fn = tmp + strlen(tmp);
if(fn[-1] != DIRSEP) { *fn++ = DIRSEP; *fn = 0; }
if(dir) {
while((de = readdir(dir))) {
i = strlen(de->d_name);
if(i == 1 && de->d_name[0] == '.') continue;
c = 0;
switch(type) {
case 0:
if(!(i > 5 && !strcmp(de->d_name + i - 4, ".png"))) continue;
break;
case 1:
strcpy(fn, de->d_name);
if(isdir(tmp)) c = '/';
else
if( (i < 5 || strcmp(de->d_name + i - 4, ".mts")) &&
(i < 5 || strcmp(de->d_name + i - 4, ".png")) &&
(i < 7 || strcmp(de->d_name + i - 6, ".schem")) &&
(i < 11 || strcmp(de->d_name + i - 10, ".schematic")) &&
(i < 4 || strcmp(de->d_name + i - 3, ".we")) &&
(i < 7 || strcmp(de->d_name + i - 6, ".we.gz")) &&
(i < 8 || strcmp(de->d_name + i - 7, ".we.bz2")) &&
(i < 5 || strcmp(de->d_name + i - 4, ".m3d")) &&
(i < 5 || strcmp(de->d_name + i - 4, ".tmx")) &&
(i < 5 || strcmp(de->d_name + i - 4, ".gox")) &&
(i < 5 || strcmp(de->d_name + i - 4, ".vox")) &&
(i < 4 || strcmp(de->d_name + i - 3, ".qb")))
continue;
break;
}
j = numfiles++;
*files = (char**)realloc(*files, numfiles * sizeof(char*));
if(!*files) error(lang[ERR_MEM]);
(*files)[j] = (char*)malloc(i + 2);
if(!(*files)[j]) error(lang[ERR_MEM]);
if(c) {
(*files)[j][0] = c;
memcpy((*files)[j] + 1, de->d_name, i + 1);
} else
memcpy((*files)[j], de->d_name, i + 1);
}
closedir(dir);
}
if(numfiles)
qsort(*files, numfiles, sizeof(char*), fncmp);
return numfiles;
}
/**
* Free directory list
*/
void freedir(int n, char ***files)
{
int i;
for(i = 0; i < n; i++)
free((*files)[i]);
free(*files);
*files = NULL;
}
/**
* Deetect cube edges
*/
void detcube(int w, int h, unsigned char *block)
{
unsigned char *d;
int i, j;
for(d = block; d[3] < 128; d += 4);
x1 = ((int)(d - block) % (w * 4)) / 4;
_y0 = (int)(d - block) / (w * 4);
for(i = 0, d = block; i < w/2 && d[3] < 128; i++)
for(j = 0, d = block + i*4; j < h/2 && d[3] < 128; j++, d += w*4);
x0 = ((int)(d - block) % (w * 4)) / 4 - 1;
_y1 = (int)(d - block) / (w * 4);
x2 = x1 + (x1 - x0) + 1;
y2 = _y1 + (_y1 - _y0);
for(; d[3] > 128; d += w*4);
d -= w*4;
y3 = (int)(d - block) / (w * 4);
y4 = y3 + (_y1 - _y0);
}
/**
* Install Minetest mod
*/
int instmod(unsigned char *data, int size)
{
FILE *f;
char tmp[MAXPATHLEN], msg[MAXPATHLEN + 128], *fn, *d;
unsigned char *un, *b, *end;
int i, j, s;
/* get the data */
b = un = (unsigned char*)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)data, size, 32768, &s, 1);
if(!un) error(lang[ERR_MEM]);
end = b + s;
#ifndef __WIN32__
if(mtsfile[0] == '~') {
strcpy(tmp, home);
strcat(tmp, mtsfile + 1);
} else
#endif
strcpy(tmp, mtsfile);
fn = tmp + strlen(tmp);
if(fn[-1] != DIRSEP) { *fn++ = DIRSEP; *fn = 0; }
strcpy(fn, "mtsedit");
fn += 7; *fn++ = DIRSEP; *fn = 0;
mkdir(tmp
#ifndef __WIN32__
, 0755
#endif
);
if(!isdir(tmp)) {
sprintf(msg, "%s: %s", lang[ERR_MOD], tmp);
error(msg);
}
while(b < end) {
#ifndef __WIN32__
i = strlen((char*)b) + 1;
#else
for(i = 0; b[i]; i++)
if(b[i] == '/') b[i] = '\\';
i++;
#endif
j = (b[i + 1] << 8) | b[i];
strcpy(fn, (char*)b);
d = strrchr(fn, DIRSEP);
if(d > fn) {
*d = 0;
mkdir(tmp
#ifndef __WIN32__
, 0755
#endif
);
*d = DIRSEP;
}
f = fopen(tmp, "wb");
if(f) {
fwrite(b + i + 2, j, 1, f);
fclose(f);
} else {
sprintf(msg, "%s: %s", lang[ERR_MOD], tmp);
error(msg);
}
b += i + 2 + j;
}
free(un);
return 0;
}
/**
* Generate slab and stair pictures from block image
*/
int stairgen()
{
FILE *f;
stbi__context sc;
stbi__result_info ri;
int i, j, w, h, l;
unsigned int size;
unsigned char *data, *block, *slab, *stair;
char *c, *d, *e, fn[MAXPATHLEN];
f = fopen(mtsfile, "rb");
if(f) {
fseek(f, 0L, SEEK_END);
size = (unsigned int)ftell(f);
fseek(f, 0L, SEEK_SET);
data = (unsigned char*)malloc(size);
if(!data) error(lang[ERR_MEM]);
fread(data, size, 1, f);
fclose(f);
c = strrchr(mtsfile, '_');
if(!c) c = strrchr(mtsfile, '.');
if(!c) c = &mtsfile[strlen(mtsfile)];
sc.read_from_callbacks = 0;
sc.img_buffer = sc.img_buffer_original = data;
sc.img_buffer_end = sc.img_buffer_original_end = data + size;
ri.bits_per_channel = 8;
block = (unsigned char*)stbi__png_load(&sc, &w, &h, &l, 0, &ri);
free(data);
if(!block) return 1;
slab = (unsigned char *)malloc(w * w * 4);
if(!slab) error(lang[ERR_MEM]);
memset(slab, 0, w * w * 4);
stair = (unsigned char *)malloc(4 * w * w * 4);
if(!stair) error(lang[ERR_MEM]);
/* detect cube edges */
detcube(w, w, block);
#if DEBUG
*((uint32_t*)(slab + _y0*w*4 + x1*4)) = 0xFF0000FF;
*((uint32_t*)(slab + _y1*w*4 + x0*4)) = 0xFF0000FF;
*((uint32_t*)(slab + _y1*w*4 + x2*4)) = 0xFF00FF00;
*((uint32_t*)(slab + y2*w*4 + x1*4)) = 0xFF00FF00;
*((uint32_t*)(slab + y3*w*4 + x0*4)) = 0xFF0000FF;
*((uint32_t*)(slab + y3*w*4 + x2*4)) = 0xFF00FF00;
*((uint32_t*)(slab + y4*w*4 + x1*4)) = 0xFF00FF00;
#endif
/* add rotated block to the bottom of the image */
if(w == h) {
block = (unsigned char*)realloc(block, 2 * w * w * 4);
if(!block) return 1;
memset(block + w * w * 4, 0, w * w * 4);
for(i = x0; i <= x2+1; i++) {
for(j = _y0; j <= y4; j++)
*((uint32_t*)(block + (32+j)*w*4 + (x2 - i + x0 + (x0 & 1))*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
data = (unsigned char *)stbi_write_png_to_mem(block, w*4, w, 2*w, 4, &l);
if(data) {
f = fopen(mtsfile,"wb");
if(f) { fwrite(data, l, 1, f); fclose(f); }
free(data);
}
}
/* slab */
memset(slab, 0, w * w * 4);
for(i = x0; i <= x1; i++) {
l = (_y1 - _y0) * (i - x0) / (x1 - x0);
for(j = _y0; j < _y1 + (y3 - _y1)/2 + l; j++)
*((uint32_t*)(slab + (j+(y3 - _y1)/2+1)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
for(j = y3 + l; j < w; j++)
*((uint32_t*)(slab + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
for(i = x1+1; i <= x2; i++) {
l = (_y1 - _y0) * ((x2-i+1) - x1) / (x2 - x1);
for(j = _y0; j < y2 + (y3 - _y1)/2 + l; j++)
*((uint32_t*)(slab + (j+(y3 - _y1)/2+1)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
for(j = y4 + l; j < w; j++)
*((uint32_t*)(slab + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
data = (unsigned char *)stbi_write_png_to_mem(slab, w*4, w, w, 4, &l);
if(data) {
strcpy(c, "_Slab.png");
f = fopen(mtsfile,"wb");
if(f) { fwrite(data, l, 1, f); fclose(f); }
free(data);
}
/* double slab */
memset(stair, 0, w * w * 4);
for(i = x0; i <= x1; i++) {
l = (_y1 - _y0) * (i - x0) / (x1 - x0);
for(j = _y0; j < _y1 + (y3 - _y1)*3/4 + l; j++)
*((uint32_t*)(stair + (j+(y3 - _y1)/4+1)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
for(j = y3 + l; j < w; j++)
*((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
for(i = x1+1; i <= x2; i++) {
l = (_y1 - _y0) * ((x2-i+1) - x1) / (x2 - x1);
for(j = _y0; j < y2 + (y3 - _y1)*3/4 + l; j++)
*((uint32_t*)(stair + (j+(y3 - _y1)/4+1)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
for(j = y4 + l; j < w; j++)
*((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
data = (unsigned char *)stbi_write_png_to_mem(stair, w*4, w, w, 4, &l);
if(data) {
strcpy(fn, mtsfile);
d = strrchr(mtsfile, DIRSEP);
if(d) d++; else d = mtsfile;
memcpy(fn, mtsfile, (int)(d - mtsfile));
e = fn + (int)(d - mtsfile);
strcpy(e, "Double_"); e += 7;
memcpy(e, d, (int)(c - d)); e += (int)(c - d);
strcpy(e, "_Slab.png");
f = fopen(fn,"wb");
if(f) { fwrite(data, l, 1, f); fclose(f); }
free(data);
}
/* rotated stairs */
memcpy(stair, slab, w * w * 4);
for(i = x0 + (x1 - x0)/2; i <= x1 + (x2 - x1)/2; i++) {
l = (_y1 - _y0) * (i - x0) / (x1 - x0);
for(j = _y0; j <= _y0 + l; j++)
*((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
for(i--; i <= x2; i++) {
for(j = _y0; j <= y4; j++)
*((uint32_t*)(stair + j*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
for(i = x0; i <= x1; i++) {
l = (_y1 - _y0) * (i - x0) / (x1 - x0);
for(j = _y1 + l - 1; j <= _y1 + (y3 - _y1)/2 + l; j++)
*((uint32_t*)(stair + (j-(_y1-_y0)/2)*w*4 + (i + (x1 - x0)/2)*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
memcpy(stair + w*w*4, slab, w * w * 4);
for(i = x0 + (x1 - x0)/2; i <= x2; i++) {
l = (_y1 - _y0) * ((x2-i+1) - x1) / (x2 - x1);
for(j = _y1 + l; j < y4; j++)
*((uint32_t*)(stair + (w+j)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
for(i = x0; i <= x1; i++) {
l = (_y1 - _y0) * ((x1-i+1) - x1) / (x1 - x0);
for(j = _y1 + l; j <= y2 + l; j++)
*((uint32_t*)(stair + (w+j+(_y1-_y0)/2)*w*4 + (i+(x1 - x0)/2)*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
memcpy(stair + 2*w*w*4, slab, w * w * 4);
for(i = x0; i <= x1 + (x2 - x1)/2; i++) {
l = (_y1 - _y0) * (i - x0) / (x1 - x0);
for(j = _y0 + l; j < y4; j++)
*((uint32_t*)(stair + (2*w+j)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
for(i = x1; i <= x2; i++) {
l = (_y1 - _y0) * (i - x1) / (x1 - x0);
for(j = _y0 + l; j <= _y0 + l + 1; j++)
*((uint32_t*)(stair + (2*w+(j+(_y1-_y0)/2))*w*4 + (i - (x1 - x0)/2)*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
memcpy(stair + 3*w*w*4, slab, w * w * 4);
for(i = x0; i <= x0 + (x1 - x0)/2 +1; i++) {
for(j = _y0; j <= y4; j++)
*((uint32_t*)(stair + (3*w+j)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
for(; i <= x2; i++) {
l = (_y1 - _y0) * (i - (x0 + (x1 - x0)/2)) / (x2 - x1);
for(j = _y0; j <= _y1 + (y2 - _y1)/2 - l; j++)
*((uint32_t*)(stair + (3*w+j)*w*4 + i*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
for(i = x1+1; i <= x2; i++) {
l = (_y1 - _y0) * ((x2-i+1) - x1) / (x2 - x1);
for(j = y2 + l - 1; j <= y2 + (y3 - _y1)/2 + l; j++)
*((uint32_t*)(stair + (3*w+(j-(_y1-_y0)/2))*w*4 + (i-(x1-x0)/2)*4)) = *((uint32_t*)(block + j*w*4 + i*4));
}
data = (unsigned char *)stbi_write_png_to_mem(stair, w*4, w, 4*w, 4, &l);
if(data) {
strcpy(c, "_Stair.png");
f = fopen(mtsfile,"wb");
if(f) { fwrite(data, l, 1, f); fclose(f); }
free(data);
}
fprintf(stderr, "mtsedit: %s\r\n", lang[IMGSGEN]);
return 0;
}
return 1;
}
/**
* Resample and save block image
*/
typedef struct tColorRGBA {
Uint8 r;
Uint8 g;
Uint8 b;
Uint8 a;
} tColorRGBA;
int savepng(char *name, int siz, SDL_Surface *src)
{
int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, lx, ly, dgap, l;
tColorRGBA *c00, *c01, *c10, *c11, *csp, *dp;
unsigned char *img;
char path[MAXPATHLEN];
FILE *f;
SDL_Surface *dst = SDL_CreateRGBSurface(0, 32, 32 * siz, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
/* resample */
src->h = siz*(TMPSIZE+8);
/**
* This is from SDL_gfx, but I don't wanted to introduce a new dependency just because
* of a single function. Heavily optimized, all non-used code-paths removed, plus I hate
* that I can't give exact destination dimensions to zoomSurfaceRGBA()
*/
sx = sy = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) error(lang[ERR_MEM]);
if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
free(sax);
error(lang[ERR_MEM]);
}
csp = (tColorRGBA *) src->pixels;
dp = (tColorRGBA *) dst->pixels;
csx = 0;
csax = sax;
for (x = 0; x <= dst->w; x++) {
*csax = csx;
csax++;
csx &= 0xffff;
csx += sx;
}
csy = 0;
csay = say;
for (y = 0; y <= dst->h; y++) {
if(!(y & 0x1F)) csy = 0;
*csay = csy;
csay++;
csy &= 0xffff;
csy += sy;
}
dgap = dst->pitch - dst->w * 4;
csay = say;
ly = 0;
for (y = l = 0; y < dst->h; y++) {
if(!(y & 0x1F)) {
csay = say;
csp = (tColorRGBA *) ((uint8_t*)src->pixels + l*(TMPSIZE+8)*src->pitch);
l++;
}
c00 = csp;
c01 = csp;
c01++;
c10 = csp;
c10 += src->pitch/4;
c11 = c10;
c11++;
csax = sax;
lx = 0;
for (x = 0; x < dst->w; x++) {
ex = (*csax & 0xffff);
ey = (*csay & 0xffff);
t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
dp->r = (((t2 - t1) * ey) >> 16) + t1;
t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
dp->g = (((t2 - t1) * ey) >> 16) + t1;
t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
dp->b = (((t2 - t1) * ey) >> 16) + t1;
t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
dp->a = (((t2 - t1) * ey) >> 16) + t1;
csax++;
if (*csax > 0)
{
sstep = (*csax >> 16);
lx += sstep;
if (lx <= src->w)
{
c00 += sstep;
c01 += sstep;
c10 += sstep;
c11 += sstep;
}
}
dp++;
}
csay++;
if (*csay > 0)
{
sstep = (*csay >> 16);
ly += sstep;
if (ly < src->h)
{
csp += (sstep * (src->pitch/4));
}
}
dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
}
free(sax);
free(say);
/* save png */
#if 0
img = (unsigned char *)stbi_write_png_to_mem(src->pixels, src->pitch, src->w, src->h, 4, &l);
#else
img = (unsigned char *)stbi_write_png_to_mem(dst->pixels, dst->pitch, dst->w, dst->h, 4, &l);
#endif
SDL_FreeSurface(dst);
if(img) {
sprintf(path, "%s.png",name);
if(verbose)
printf("mtsedit: %s\r\n", path);
f = fopen(path,"wb");
if(f) { fwrite(img, l, 1, f); fclose(f); }
free(img);
return 1;
}
return 0;
}
/**
* Mix two pixels
*/
void mixpixel(unsigned char *a, unsigned char *b, unsigned int i)
{
unsigned int c[4];
c[0] = (unsigned int)b[0] * i / 255; if(c[0] > 255) c[0] = 255;
c[1] = (unsigned int)b[1] * i / 255; if(c[1] > 255) c[1] = 255;
c[2] = (unsigned int)b[2] * i / 255; if(c[2] > 255) c[2] = 255;
a[0] = (c[0]*b[3] + (256 - a[3])*a[0])>>8;
a[1] = (c[1]*b[3] + (256 - a[3])*a[1])>>8;
a[2] = (c[2]*b[3] + (256 - a[3])*a[2])>>8;
a[3] = (b[3]*b[3] + (256 - a[3])*a[3])>>8;
}
/**
* Isometric blit
*/
void blitiso(unsigned char *from, int quadrant, unsigned char *to, int rotation, int x, int y, int blk)
{
int i, j, ox, oy, sx, sy;
x += 8; y += 4 + blk*(TMPSIZE+8);
switch(quadrant) {
/* XX
* XX (full) */
case 0: sx = sy = 32; ox = oy = 0; break;
/* X.
* .. */
case 1: sx = sy = 16; ox = oy = 0; break;
/* .X
* .. */
case 2: sx = sy = 16; ox = 16; oy = 0; break;
/* ..
* X. */
case 3: sx = sy = 16; oy = 16; ox = 0; break;
/* ..
* .X */
case 4: sx = sy = 16; ox = oy = 16; break;
/* ..
* __ (slabs) */
case 5: sx = 32; sy = 8; ox = 0; oy = 24; break;
}
switch(rotation) {
/* .N.
* XXN
* .X. */
case 0:
for(j = 0; j < sy; j++)
for(i = 0; i <= j; i++) {
mixpixel(to + (y+j/2)*TMPSIZE*4 + (x+2*i+sx-j-1)*4, from + (oy+j-i)*32*4 + (ox+i)*4, 287);
mixpixel(to + (y+j/2)*TMPSIZE*4 + (x+2*i+sx-j)*4, from + (oy+j-i)*32*4 + (ox+i)*4, 287);
}
for(j = 0; j < sy; j++)
for(i = 0; i <= j; i++) {
mixpixel(to + (y+sy-j/2-1)*TMPSIZE*4+(x+2*sx-(2*i+sx-j)-1)*4, from + (oy+sy-j-1+i)*32*4+(ox+sx-i-1)*4, 287);
mixpixel(to + (y+sy-j/2-1)*TMPSIZE*4+(x+2*sx-(2*i+sx-j))*4, from + (oy+sy-j-1+i)*32*4+(ox+sx-i-1)*4, 287);
}
break;
/* .X.
* XXN
* .N. */
case 1:
for(j = 0; j < sy; j++)
for(i = 0; i <= j; i++) {
mixpixel(to + (y+j/2)*TMPSIZE*4 + (x+2*i+sx-j-1)*4, from + (oy+sy-i-1)*32*4 + (ox+j-i)*4, 287);
mixpixel(to + (y+j/2)*TMPSIZE*4 + (x+2*i+sx-j)*4, from + (oy+sy-i-1)*32*4 + (ox+j-i)*4, 287);
}
for(j = 0; j < sy; j++)
for(i = 0; i <= j; i++) {
mixpixel(to + (y+sy-j/2-1)*TMPSIZE*4 + (x+2*sx-(2*i+sx-j)-1)*4, from + (oy+i)*32*4 + (ox+sx-(j-i)-1)*4, 287);
mixpixel(to + (y+sy-j/2-1)*TMPSIZE*4 + (x+2*sx-(2*i+sx-j))*4, from + (oy+i)*32*4 + (ox+sx-(j-i)-1)*4, 287);
}
break;
/* .X.
* NXX
* .N. */
case 2:
for(j = 0; j < sy; j++)
for(i = 0; i <= j; i++) {
mixpixel(to + (y+j/2)*TMPSIZE*4 + (x+2*i+sx-j-1)*4, from + (oy+sy-j+i-1)*32*4 + (ox+sx-i-1)*4, 287);
mixpixel(to + (y+j/2)*TMPSIZE*4 + (x+2*i+sx-j)*4, from + (oy+sy-j+i-1)*32*4 + (ox+sx-i-1)*4, 287);
}
for(j = 0; j < sy; j++)
for(i = 0; i <= j; i++) {
mixpixel(to + (y+sy-j/2-1)*TMPSIZE*4 + (x+2*sx-(2*i+sx-j)-1)*4, from + (oy+j-i)*32*4 + (ox+i)*4, 287);
mixpixel(to + (y+sy-j/2-1)*TMPSIZE*4 + (x+2*sx-(2*i+sx-j))*4, from + (oy+j-i)*32*4 + (ox+i)*4, 287);
}
break;
/* .N.
* NXX
* .X. */
case 3:
for(j = 0; j < sy; j++)
for(i = 0; i <= j; i++) {
mixpixel(to + (y+j/2)*TMPSIZE*4 + (x+2*i+sx-j-1)*4, from + (oy+i)*32*4 + (ox+sx-(j-i)-1)*4, 287);
mixpixel(to + (y+j/2)*TMPSIZE*4 + (x+2*i+sx-j)*4, from + (oy+i)*32*4 + (ox+sx-(j-i)-1)*4, 287);
}
for(j = 0; j < sy; j++)
for(i = 0; i <= j; i++) {
mixpixel(to + (y+sy-j/2-1)*TMPSIZE*4 + (x+2*sx-(2*i+sx-j)-1)*4, from + (oy+sy-i-1)*32*4 + (ox+j-i)*4, 287);
mixpixel(to + (y+sy-j/2-1)*TMPSIZE*4 + (x+2*sx-(2*i+sx-j))*4, from + (oy+sy-i-1)*32*4 + (ox+j-i)*4, 287);
}
break;
/* X.
* XX
* .X */
case 4:
for(j = 0; j < sy*2; j++)
for(i = 0; i < sx; i++)
mixpixel(to + (y+j*5/8+i/2)*TMPSIZE*4 + (x+i)*4, from + (oy+j/2)*32*4 + (ox+i)*4, 255);
break;
/* .X
* XX
* X. */
case 5:
for(j = 0; j < sy*2; j++)
for(i = 0; i < sx; i++)
mixpixel(to + (y+j*5/8+sy/2-i/2-1)*TMPSIZE*4 + (x+i)*4, from + (oy+j/2)*32*4 + (ox+i)*4, 128);
break;
/* XX
* XX */
default:
for(j = 0; j < sy; j++)
for(i = 0; i < sx; i++) {
mixpixel(to + (y+2*j)*TMPSIZE*4 + (x+2*i)*4, from + (oy+j)*32*4 + (ox+i)*4, 255);
mixpixel(to + (y+2*j)*TMPSIZE*4 + (x+2*i+1)*4, from + (oy+j)*32*4 + (ox+i)*4, 255);
mixpixel(to + (y+2*j+1)*TMPSIZE*4 + (x+2*i)*4, from + (oy+j)*32*4 + (ox+i)*4, 255);
mixpixel(to + (y+2*j+1)*TMPSIZE*4 + (x+2*i+1)*4, from + (oy+j)*32*4 + (ox+i)*4, 255);
}
break;
}
}
/**
* Generate block, slab and stair pictures from textures
*/
int blockgen(char *blockid)
{
stbi__context sc;
stbi__result_info ri;
FILE *f;
unsigned char *img, *raw, *top, *bottom, *north, *south, *west, *east, out[32 * TMPSIZE * (TMPSIZE+8) * 4];
unsigned int size, *tmp;
char *data, c, *s, *e, **paths = NULL, path[MAXPATHLEN];
int w, h, l, r, p = 0, np = 0, i, j = 0, k = 0, o = 0, sl, ds, st;
SDL_Rect src, dst;
fg = SDL_CreateRGBSurfaceFrom(NULL, 32, 32, 32, 32*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
src.x = src.y = dst.x = dst.y = 0; dst.w = dst.h = 32;
f = fopen(mtsfile, "rb");
if(f) {
fseek(f, 0L, SEEK_END);
size = (unsigned int)ftell(f);
fseek(f, 0L, SEEK_SET);
data = (char*)malloc(size + 1);
if(!data) error(lang[ERR_MEM]);
fread(data, size, 1, f);
data[size] = 0;
fclose(f);
} else {
fprintf(stderr, "mtsedit: %s: %s\r\n", lang[ERR_IMGCSV], mtsfile);
return 1;
}
for(s = blockid; s && *s; s++)
if(*s == ' ' || *s == '\'') *s = '_';
e = data;
while(*e) {
if(e == data || *e == '\r' || *e == '\n') {
while(*e == '\r' || *e == '\n') e++;
if(!*e) break;
/* get canonical name */
if(*e == '\"') { e++; c = '\"'; } else c = ',';
for(s = e; *e && *e != '\r' && *e != '\n' && *e != c; e++)
if(*e == '\\' || (*e == '\"' && e[1] == '\"')) { e++; continue; }
while(*s <= ' ') s++;
while(*(e-1) <= ' ') e--;
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].blocknames = (char**)malloc(16 * sizeof(char*));
if(!blocks[j].blocknames) error(lang[ERR_MEM]);
memset(blocks[j].blocknames, 0, 16 * sizeof(char*));
blocks[j].name = (char*)malloc((int)(e - s) + 1);
if(!blocks[j].name) error(lang[ERR_MEM]);
for(i = 0; i < (int)(e - s); i++)
blocks[j].name[i] = s[i] == ' ' || s[i] == '\'' ? '_' : s[i];
while(i > 0 && (blocks[j].name[i-1] == '(' || blocks[j].name[i-1] == '_')) i--;
blocks[j].name[i] = 0;
/* get technical name, drawtype, paramtype2, node_box type */
for(k = 0; k < 4; k++) {
while(*e && *e != ',' && *e != ';' && *e != '\t' && *e != '\r' && *e != '\n') e++;
if(*e != ',' && *e != ';' && *e != '\t') errorimgcsv(j+1);
e++;
while(*e == ' ') e++;
if(*e == '\"') { e++; c = '\"'; } else c = ',';
for(s = e; *e && *e != '\r' && *e != '\n' && *e != c; e++)
if(*e == '\\' || (*e == '\"' && e[1] == '\"')) { e++; continue; }
while(*s <= ' ') s++;
while(*(e-1) <= ' ') e--;
blocks[j].blocknames[k] = (char*)malloc(e - s + 1);
if(!blocks[j].blocknames[k]) error(lang[ERR_MEM]);
memcpy(blocks[j].blocknames[k], s, e - s + 1);
blocks[j].blocknames[k][e - s] = 0;
}
/* get directory */
while(*e && *e != ',' && *e != ';' && *e != '\t' && *e != '\r' && *e != '\n') e++;
if(*e != ',' && *e != ';' && *e != '\t') errorimgcsv(j+1);
e++;
while(*e == ' ') e++;
if(*e == '\"') { e++; c = '\"'; } else c = ',';
for(s = e; *e && *e != '\r' && *e != '\n' && *e != c; e++)
if(*e == '\\' || (*e == '\"' && e[1] == '\"')) { e++; continue; }
while(*s <= ' ') s++;
while(*(e-1) <= ' ') e--;
memcpy(path, s, e - s + 1);
k = e - s;
if(path[k-1] != DIRSEP) { path[k++] = DIRSEP; path[k] = 0; }
strcpy(path + k, "textures"); k += 8;
path[k++] = DIRSEP; path[k] = 0;
for(k = 0, o = -1; k < np; k++)
if(!strcmp(paths[k], path)) { o = k; break; }
if(o == -1) {
paths = (char**)realloc(paths, (np + 1) * sizeof(char*));
if(!paths) error(lang[ERR_MEM]);
paths[np] = (char*)malloc(strlen(path) + 1);
if(!paths[np]) error(lang[ERR_MEM]);
strcpy(paths[np], path);
np++;
}
while(*e && *e != ',' && *e != ';' && *e != '\t' && *e != '\r' && *e != '\n') e++;
k = 0; o = 0;
}
/* read textures */
while(*e == ' ') e++;
if(*e != ',' && *e != ';' && *e != '\t') errorimgcsv(j+1);
e++;
while(*e == ' ') e++;
if(!*e) errorimgcsv(j+1);
if(*e == '\"') { e++; c = '\"'; } else c = ',';
for(s = e; *e && *e != '\r' && *e != '\n' && *e != c; e++)
if(*e == '\\' || (*e == '\"' && e[1] == '\"')) { e++; continue; }
while(*s <= ' ') s++;
while(*(e-1) <= ' ') e--;
/* switch to overlay textures */
if(s[0] == '|') { k = 10; continue; }
/* read in the texture filenames */
if(e != s) {
for(i = 0; i < e - s && s[i] != '^'; i++);
blocks[j].blocknames[k + 4] = (char*)malloc(i + 1);
if(!blocks[j].blocknames[k + 4]) error(lang[ERR_MEM]);
memcpy(blocks[j].blocknames[k + 4], s, i);
blocks[j].blocknames[k + 4][i] = 0;
}
k++; if(k > 15) errorimgcsv(j+1);
while(*e && *e != ',' && *e != ';' && *e != '\t' && *e != '\r' && *e != '\n') e++;
}
/* we have all possible paths, read in the textures */
for(i = 0; i < j; i++) {
/* filter out what we can't or don't want to handle */
if(blockid && strcasecmp(blockid, blocks[i].name)) continue;
s = strrchr(blocks[i].name, '_');
if(s && (!strcmp(s, "_Slab") || !strcmp(s, "_Stair"))) continue;
if(!strcmp(blocks[i].blocknames[1], "nodebox") || !strcmp(blocks[i].blocknames[1], "mesh")) continue;
/* get tiles */
for(k = 0; k < 6; k++) {
if(blocks[i].blocknames[k + 4] && blocks[i].blocknames[k + 4][0])
for(l = r = 0; l < np && !r; l++) {
strcpy(path, paths[l]);
strcat(path, blocks[i].blocknames[k + 4]);
f = fopen(path, "rb");
if(f) {
if(verbose && blockid) printf("mtsedit: texture %s\n",path);
fseek(f, 0L, SEEK_END);
size = (unsigned int)ftell(f);
fseek(f, 0L, SEEK_SET);
img = (unsigned char*)malloc(size);
if(!img) error(lang[ERR_MEM]);
fread(img, size, 1, f);
fclose(f);
sc.read_from_callbacks = 0;
sc.img_buffer = sc.img_buffer_original = img;
sc.img_buffer_end = sc.img_buffer_original_end = img + size;
ri.bits_per_channel = 8;
raw = (unsigned char*)stbi__png_load(&sc, &w, &h, &r, 0, &ri);
if(raw) {
/* convert grayscale, alpha grayscale and RGB to RGBA */
if(r != 4) {
tmp = (unsigned int *)malloc(w * h * 4);
if(!tmp) error(lang[ERR_MEM]);
for(l = 0; l < w * h; l++)
switch(r) {
case 1: tmp[l] = 0xFF000000 | (raw[l] << 16) | (raw[l] << 8) | (raw[l]); break;
case 2: tmp[l] = (raw[(l<<1)+1]<<24) | (raw[l<<1]<<16) | (raw[l<<1]<<8) | (raw[l<<1]); break;
case 3: tmp[l] = 0xFF000000 | (raw[l*3+2] << 16) | (raw[l*3+1] << 8) | (raw[l*3]); break;
}
free(raw);
raw = (unsigned char*)tmp;
}
/* rescale to 32x32 */
if(!blocks[i].img) {
blocks[i].img = (unsigned char *)malloc(6 * 32 * 32 * 4);
if(!blocks[i].img) error(lang[ERR_MEM]);
memset(blocks[i].img, 0, 6 * 32 * 32 * 4);
}
fg->pixels = blocks[i].img + k * 32 * 32 * 4;
blk = SDL_CreateRGBSurfaceFrom(raw, w, h, 32, w*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
src.w = w; src.h = h;
SDL_BlitScaled(blk, &src, fg, &dst);
SDL_FreeSurface(blk);
free(raw);
blocks[i].numpar2++;
} else r = 0;
free(img);
}
}
/* get overlays */
if(blocks[i].img && blocks[i].blocknames[k + 10] && blocks[i].blocknames[k + 10][0])
for(l = r = 0; l < np && !r; l++) {
strcpy(path, paths[l]);
strcat(path, blocks[i].blocknames[k + 10]);
f = fopen(path, "rb");
if(f) {
fseek(f, 0L, SEEK_END);
size = (unsigned int)ftell(f);
fseek(f, 0L, SEEK_SET);
img = (unsigned char*)malloc(size);
if(!img) error(lang[ERR_MEM]);
fread(img, size, 1, f);
fclose(f);
sc.read_from_callbacks = 0;
sc.img_buffer = sc.img_buffer_original = img;
sc.img_buffer_end = sc.img_buffer_original_end = img + size;
ri.bits_per_channel = 8;
raw = (unsigned char*)stbi__png_load(&sc, &w, &h, &r, 0, &ri);
if(raw) {
/* convert grayscale, alpha grayscale and RGB to RGBA */
if(r != 4) {
tmp = (unsigned int *)malloc(w * h * 4);
if(!tmp) error(lang[ERR_MEM]);
for(l = 0; l < w * h; l++)
switch(r) {
case 1: tmp[l] = 0xFF000000 | (raw[l] << 16) | (raw[l] << 8) | (raw[l]); break;
case 2: tmp[l] = (raw[(l<<1)+1]<<24) | (raw[l<<1]<<16) | (raw[l<<1]<<8) | (raw[l<<1]); break;
case 3: tmp[l] = 0xFF000000 | (raw[l*3+2] << 16) | (raw[l*3+1] << 8) | (raw[l*3]); break;
}
free(raw);
raw = (unsigned char*)tmp;
}
/* rescale to 32x32 */
fg->pixels = blocks[i].img + k * 32 * 32 * 4;
blk = SDL_CreateRGBSurfaceFrom(raw, w, h, 32, w*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
src.w = w; src.h = h;
/* blit overlay to original texture */
SDL_BlitScaled(blk, &src, fg, &dst);
SDL_FreeSurface(blk);
free(raw);
} else r = 0;
free(img);
}
}
}
if(blocks[i].img) p++;
}
SDL_FreeSurface(fg);
blk = SDL_CreateRGBSurfaceFrom(out, TMPSIZE, 32*TMPSIZE, 32, TMPSIZE*4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
/* ok, now we have all the textures loaded. The blocks array looks like this:
* name = canonical block id name (like "Acacia_Bark_Stair")
* blocknames[0] = block's technical name (like "mcl_core:bedrock")
* blocknames[1] = drawtype
* blocknames[2] = paramtype2
* blocknames[3] = node_box type
* blocknames[4] - [9] = tile names (like "acacia.png^[transformR90")
* blocknames[10] - [15] = overlay tile names
* img[0] - img[5] = uncompressed tiles, scaled to 32x32 RGBA, overlay images applied
* numpar2 = number of textures */
for(i = k = o = 0; i < j; i++) {
if(!blocks[i].img) continue;
if(!verbose) {
k++;
printf("%3d%% %s \r", k*100/p, blocks[i].name);
}
/* construct block images */
memset(out, 0, sizeof(out));
if(!strcmp(blocks[i].blocknames[1], "plantlike") || !strcmp(blocks[i].blocknames[1], "firelike")) {
blitiso(blocks[i].img, 0, out, 6, 0,0, 0);
o += savepng(blocks[i].name,1,blk);
} else
if(!strcmp(blocks[i].blocknames[1], "raillike")) {
blitiso(blocks[i].img, 0, out, 0, 0,40, 0);
blitiso(blocks[i].img, 0, out, 1, 0,40, 1);
o += savepng(blocks[i].name,2,blk);
} else
if(!strcmp(blocks[i].blocknames[1], "normal") || !strcmp(blocks[i].blocknames[1], "glasslike")) {
/* get side textures */
top = blocks[i].img;
bottom = blocks[i].numpar2 > 1 ? blocks[i].img + 32 * 32 * 4 : blocks[i].img;
west = blocks[i].numpar2 > 2 ? blocks[i].img + 2 * 32 * 32 * 4 : blocks[i].img;
east = blocks[i].numpar2 > 3 ? blocks[i].img + 3 * 32 * 32 * 4 : west;
north = blocks[i].numpar2 > 4 ? blocks[i].img + 4 * 32 * 32 * 4 : west;
south = blocks[i].numpar2 > 5 ? blocks[i].img + 5 * 32 * 32 * 4 : west;
/* faces south */
blitiso(bottom, 0, out, 0, 0,39, 0);
blitiso(west, 0, out, 5, 0,0, 0);
blitiso(north, 0, out, 4, 32,0, 0);
blitiso(east, 0, out, 5, 32,16, 0);
blitiso(south, 0, out, 4, 0,16, 0);
blitiso(top, 0, out, 0, 0,0, 0);
l = 1;
if(blocks[i].numpar2 > 1) {
/* faces east */
blitiso(bottom, 0, out, 1, 0,39, 1);
blitiso(south, 0, out, 5, 0,0, 1);
blitiso(west, 0, out, 4, 32,0, 1);
blitiso(north, 0, out, 5, 32,16, 1);
blitiso(east, 0, out, 4, 0,16, 1);
blitiso(top, 0, out, 1, 0,0, 1);
l++;
/* faces north */
blitiso(bottom, 0, out, 2, 0,39, 2);
blitiso(east, 0, out, 5, 0,0, 2);
blitiso(south, 0, out, 4, 32,0, 2);
blitiso(west, 0, out, 5, 32,16, 2);
blitiso(north, 0, out, 4, 0,16, 2);
blitiso(top, 0, out, 2, 0,0, 2);
l++;
/* faces west */
blitiso(bottom, 0, out, 3, 0,39, 3);
blitiso(north, 0, out, 5, 0,0, 3);
blitiso(east, 0, out, 4, 32,0, 3);
blitiso(south, 0, out, 5, 32,16, 3);
blitiso(west, 0, out, 4, 0,16, 3);
blitiso(top, 0, out, 3, 0,0, 3);
l++;
}
o += savepng(blocks[i].name,l,blk);
/* check if we need to generate slabs and stairs for this block */
/* FIXME: this assumes no block ids are longer than 255 bytes, hopefully true */
sprintf(path, "%s_Slab", blocks[i].name);
sprintf(path+256, "Double_%s_Slab", blocks[i].name);
sprintf(path+512, "%s_Stair", blocks[i].name);
for(sl = ds = st = l = 0; l < j; l++) {
if(!strcmp(blocks[l].name, path)) sl = 1;
if(!strcmp(blocks[l].name, path+256)) ds = 1;
if(!strcmp(blocks[l].name, path+512)) st = 1;
}
if(sl) {
/* slab */
memset(out, 0, sizeof(out));
blitiso(bottom, 0, out, 0, 0,39, 0);
blitiso(west, 5, out, 5, 0,41, 0);
blitiso(north, 5, out, 4, 32,30, 0);
blitiso(east, 5, out, 5, 32,58, 0);
blitiso(south, 5, out, 4, 0,46, 0);
blitiso(top, 0, out, 0, 0,29, 0);
o += savepng(path,1,blk);
}
if(ds) {
/* double slab */
memset(out, 0, sizeof(out));
blitiso(bottom, 0, out, 0, 0,39, 0);
blitiso(west, 3, out, 5, 0,28, 0);
blitiso(west, 4, out, 5, 16,20, 0);
blitiso(north, 3, out, 4, 32,20, 0);
blitiso(north, 4, out, 4, 48,28, 0);
blitiso(east, 4, out, 5, 48,36, 0);
blitiso(east, 3, out, 5, 32,44, 0);
blitiso(south, 4, out, 4, 16,44, 0);
blitiso(south, 3, out, 4, 0,36, 0);
blitiso(top, 0, out, 0, 0,20, 0);
o += savepng(path+256,1,blk);
}
if(st) {
/* stairs */
memset(out, 0, sizeof(out));
/* north */
blitiso(bottom, 0, out, 0, 0,39, 0);
blitiso(west, 3, out, 5, 0,28, 0);
blitiso(west, 4, out, 5, 16,20, 0);
blitiso(north, 3, out, 4, 32,20, 0);
blitiso(north, 4, out, 4, 48,28, 0);
blitiso(east, 4, out, 5, 48,36, 0);
blitiso(east, 3, out, 5, 32,44, 0);
blitiso(south, 4, out, 4, 16,44, 0);
blitiso(south, 3, out, 4, 0,36, 0);
blitiso(top, 1, out, 0, 16,36, 0);
blitiso(top, 2, out, 0, 0,28, 0);
blitiso(west, 2, out, 5, 16,1, 0);
blitiso(north, 1, out, 4, 32,1, 0);
blitiso(north, 2, out, 4, 48,9, 0);
blitiso(east, 2, out, 5, 48,17, 0);
blitiso(south, 2, out, 4, 32,17, 0);
blitiso(south, 1, out, 4, 16,9, 0);
blitiso(top, 4, out, 0, 16,0, 0);
blitiso(top, 3, out, 0, 32,8, 0);
/* east */
blitiso(bottom, 0, out, 1, 0,39, 1);
blitiso(south, 3, out, 5, 0,28, 1);
blitiso(south, 4, out, 5, 16,20, 1);
blitiso(west, 3, out, 4, 32,20, 1);
blitiso(west, 4, out, 4, 48,28, 1);
blitiso(north, 4, out, 5, 48,36, 1);
blitiso(north, 3, out, 5, 32,44, 1);
blitiso(east, 4, out, 4, 16,44, 1);
blitiso(east, 3, out, 4, 0,36, 1);
blitiso(top, 3, out, 1, 16,20, 1);
blitiso(top, 4, out, 1, 0,28, 1);
blitiso(south, 1, out, 5, 32,9, 1);
blitiso(south, 2, out, 5, 16,17, 1);
blitiso(west, 2, out, 4, 48,9, 1);
blitiso(north, 2, out, 5, 48,18, 1);
blitiso(north, 1, out, 5, 32,26, 1);
blitiso(east, 2, out, 4, 16,26, 1);
blitiso(top, 1, out, 1, 32,8, 1);
blitiso(top, 2, out, 1, 16,16, 1);
/* south */
blitiso(bottom, 0, out, 2, 0,39, 2);
blitiso(east, 3, out, 5, 0,28, 2);
blitiso(east, 4, out, 5, 16,20, 2);
blitiso(south, 3, out, 4, 32,20, 2);
blitiso(south, 4, out, 4, 48,28, 2);
blitiso(west, 4, out, 5, 48,36, 2);
blitiso(west, 3, out, 5, 32,44, 2);
blitiso(north, 4, out, 4, 16,44, 2);
blitiso(north, 3, out, 4, 0,36, 2);
blitiso(top, 1, out, 2, 16,20, 2);
blitiso(top, 2, out, 2, 32,28, 2);
blitiso(east, 1, out, 5, 0,9, 2);
blitiso(south, 1, out, 4, 16,9, 2);
blitiso(south, 2, out, 4, 32,17, 2);
blitiso(west, 1, out, 5, 32,25, 2);
blitiso(north, 2, out, 4, 16,25, 2);
blitiso(north, 1, out, 4, 0,17, 2);
blitiso(top, 3, out, 2, 0,8, 2);
blitiso(top, 4, out, 2, 16,16, 2);
/* west */
blitiso(bottom, 0, out, 3, 0,39, 3);
blitiso(north, 3, out, 5, 0,28, 3);
blitiso(north, 4, out, 5, 16,20, 3);
blitiso(east, 3, out, 4, 32,20, 3);
blitiso(east, 4, out, 4, 48,28, 3);
blitiso(south, 4, out, 5, 48,36, 3);
blitiso(south, 3, out, 5, 32,44, 3);
blitiso(west, 4, out, 4, 16,44, 3);
blitiso(west, 3, out, 4, 0,36, 3);
blitiso(top, 1, out, 3, 32,28, 3);
blitiso(top, 2, out, 3, 16,36, 3);
blitiso(north, 1, out, 5, 0,9, 3);
blitiso(north, 2, out, 5, 16,1, 3);
blitiso(east, 1, out, 4, 32,1, 3);
blitiso(south, 2, out, 5, 32,9, 3);
blitiso(south, 1, out, 5, 16,17, 3);
blitiso(west, 1, out, 4, 0,17, 3);
blitiso(top, 3, out, 3, 0,8, 3);
blitiso(top, 4, out, 3, 16,0, 3);
o += savepng(path+512,4,blk);
}
}
}
if(!verbose)
printf("mtsedit: %d PNGs, OK. \r\n", o);
return 0;
}
/**
* Returns true if color is not used by any blocks
*/
int is_color_unique(uint32_t color)
{
int i;
if(color == 0xFF000000 || color == 0xFFFF0000 || color == theme[THEME_BP_BG]) return 0;
for(i = 1; i < numblocks - 1; i++)
if(blocks[i].color == color) return 0;
return 1;
}
/**
* Returns block id for the closest color match
*/
int color2blockid(uint32_t color)
{
unsigned char *c = (unsigned char*)&color, *d;
int i, j, k, ld, diff = 255;
/* unknown and air blocks */
if(color == 0xFFFF0000 || color == theme[THEME_BP_BG] || c[3] < 128 || (!c[0] && !c[1] && !c[2])) return 0;
for(i = 1, j = 0; i < numblocks; i++) {
/* get local maximum */
ld = 0; d = (unsigned char*)&blocks[i].color;
k = d[0] > c[0] ? d[0] - c[0] : c[0] - d[0]; if(k > ld) ld = k;
k = d[1] > c[1] ? d[1] - c[1] : c[1] - d[1]; if(k > ld) ld = k;
k = d[2] > c[2] ? d[2] - c[2] : c[2] - d[2]; if(k > ld) ld = k;
k = d[3] > c[3] ? d[3] - c[3] : c[3] - d[3]; if(k > ld) ld = k;
/* is it smaller than global minimum? */
if(ld < diff) { diff = ld; j = i; }
}
return j;
}
/**
* Generate GIMP palette for blocks with unique colors
*/
void gimppal()
{
int i;
unsigned char *c;
blocks_parse();
printf("GIMP Palette\r\nName: Minetest\r\n# Generated by MTSEdit with current blocks.csv for Goxel\r\n");
for(i = 0; i < numblocks; i++) {
c = (unsigned char*)&blocks[i].color;
if(c[3])
printf("%3d %3d %3d\t#%06x\r\n", c[2], c[1], c[0], blocks[i].color & 0xFFFFFF);
}
}
/**
* Generate colors.txt for Minetestmapper
*/
void mapperpal()
{
int i;
unsigned char *c;
if(savepal < 0 || savepal >= numpalettes) savepal = 0;
printf("# Generated by MTSEdit for Minetestmapper colors.txt\r\n# using node palette %s\r\n\r\n", palettes[savepal]);
for(i = 0; i < numblocks; i++) {
c = (unsigned char*)&blocks[i].color;
if(c[3] && blocks[i].blocknames[3])
printf("%s %3d %3d %3d\r\n", blocks[i].blocknames[savepal+3] && blocks[i].blocknames[savepal+3][0] ?
blocks[i].blocknames[savepal+3] : blocks[i].blocknames[3], c[0], c[1], c[2]);
}
}
/**
* Generate tile remapping list
*/
void tileremap()
{
int i, j, k;
/* make sure no translations are loaded and no remapping used */
noblocknametranslate = 1;
remapfile = NULL;
blocks_parse();
for(j = 0; j < 256; j++)
for(i = 0; i < numblocks; i++)
if(blocks[i].blockids[0] == j || (j && blocks[i].blockids[1] == j)) {
for(k = 0; k < (int)strlen(blocks[i].name); k++)
if(blocks[i].name[k] == ' ') blocks[i].name[k] = '_';
printf("%s\r\n", blocks[i].name);
/* printf("0x%08x\n",blocks[i].color);*/
break;
}
}