diff --git a/docs/batch.md b/docs/batch.md index 0aaf6ef..5e406f1 100644 --- a/docs/batch.md +++ b/docs/batch.md @@ -8,7 +8,7 @@ See also the [GUI usage](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/usag ``` MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license -./mtsedit [-h] [-v] [-l lang] [-d|-p|-P|-b] [-m map] <.mts|.schematic> [out.mts] +./mtsedit [-h] [-v] [-l lang] [-d|-p|-P|-b|-B] [-m map] <.mts|.schematic> [out.mts] ./mtsedit -g ./mtsedit -t [blockid] ./mtsedit -i [Minetest mods dir] @@ -18,7 +18,7 @@ MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license -d: dump layers to output -p: save preview -P: save preview, cut structure in half - -b: save blueprint + -b, -B: save blueprint -m map: replace block type mapping -g: generate slab and stairs from block image -t: generate block images from texture data @@ -38,15 +38,17 @@ This will print out mapping and each block as hex values layer by layer. ./mtsedit -d structure.mts ``` -### Generate blueprint +### Generate Blueprint Pretty much the same as dump, but instead of dumping the MTS to the console, it saves "structure_bp.png". ``` ./mtsedit -b structure.mts ``` -Each layer will be padded by 1 x 1 pixels of transparency. On the left, there's a black pixel after each layer so +Each layer will be padded by 1 x 1 pixels of transparency. On the left, there's a black pixel at each layer start so that programs can detect the dimensions from the image. On the right, there's a red line for the ground layer. +The `-b` flag will also save a blue background and block legend on the image. `-B` only saves the raw blueprint. + ### Conversion (or Remap) You can convert Minecraft NBT Schematics with this tool. diff --git a/docs/import.md b/docs/import.md new file mode 100644 index 0000000..5792722 --- /dev/null +++ b/docs/import.md @@ -0,0 +1,23 @@ +Import File Formats +=================== + +This document describes the format that MTSEdit can import schematics from. For the native format, that can be written too, see +the [MTS format](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/mts_format.md). + +Schematic +--------- + +These are [Minecraft NBT](https://minecraft.gamepedia.com/Schematic_file_format) files, used by many Minecraft-compatible +editors. Only the [blocks](https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening/Block_IDs) section is parsed, +and only used to import schematics. MTSEdit does not save .schematic files. + +Limitations: rotation info are not imported properly, but works for basic nodes most of the time. + +Blueprint PNG +------------- + +MTSEdit is capable to save schematic blueprints to PNG files using the `-b` or `-B` flags. These can be imported too. +If blueprint is not detected on the image, then the entire image is read as a one layer blueprint finding the blocks that +are closest match in color to each pixel. + +Limitations: rotation info is not stored in blueprint images at all. diff --git a/docs/mts_format.md b/docs/mts_format.md index 45aec31..a11550d 100644 --- a/docs/mts_format.md +++ b/docs/mts_format.md @@ -1,12 +1,8 @@ -File Formats -============ +MTS File Format +=============== -Schematic ---------- - -These are [Minecraft NBT](https://minecraft.gamepedia.com/Schematic_file_format) files, used by many Minecraft-compatible -editors. Only the [blocks](https://minecraft.gamepedia.com/Java_Edition_data_values/Pre-flattening/Block_IDs) section is parsed, -and only used to import schematics. MTSEdit does not save .schematic files. +This document describes the MTS format native to MTSEdit and the Minetest Engine. For other formats that are supported +by MTSEdit to import schematics, see [import formats](https://gitlab.com/bztsrc/mtsedit/blob/master/docs/import.md). MTS --- diff --git a/docs/usage.md b/docs/usage.md index 8162ee3..622bf50 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -117,6 +117,7 @@ Main editor window: | P | save preview PNG | | Shift + P | save preview with structure cut in half | | E | export blueprint PNG | +| Shift + E | export plain blueprint without legend | | R | change orientation, rotate entire structure CCW | | Shift + R | change orientation, rotate entire structure CW | | F | flip the entire structure along the Z axis | diff --git a/examples/Notre_Dame_bp.png b/examples/Notre_Dame_bp.png index 4690639..872252a 100644 Binary files a/examples/Notre_Dame_bp.png and b/examples/Notre_Dame_bp.png differ diff --git a/examples/Old_Mine_bp.png b/examples/Old_Mine_bp.png index 4e97c7d..a5baead 100644 Binary files a/examples/Old_Mine_bp.png and b/examples/Old_Mine_bp.png differ diff --git a/examples/Pantheon_bp.png b/examples/Pantheon_bp.png index 6f6fb9b..027541a 100644 Binary files a/examples/Pantheon_bp.png and b/examples/Pantheon_bp.png differ diff --git a/examples/Skull_island_v1_bp.png b/examples/Skull_island_v1_bp.png index 4c4595e..f875fb4 100644 Binary files a/examples/Skull_island_v1_bp.png and b/examples/Skull_island_v1_bp.png differ diff --git a/src/Makefile b/src/Makefile index b074795..bdc00f2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -41,7 +41,7 @@ endif endif endif -CFLAGS += -Wall -Wextra -ansi -pedantic -D_$(PACKAGE)_=1 +CFLAGS += -Wall -Wextra -ansi -pedantic -D_$(PACKAGE)_=1 -g all: configure data.h $(OBJS) $(TARGET) diff --git a/src/blocks.c b/src/blocks.c index dc0b363..6a3f2c5 100644 --- a/src/blocks.c +++ b/src/blocks.c @@ -244,6 +244,7 @@ void blocks_parse() sc.img_buffer_end = sc.img_buffer_original_end = img + size; ri.bits_per_channel = 8; blocks[j].img = (unsigned char*)stbi__png_load(&sc, &w, &h, &r, 0, &ri); + free(img); if(blocks[j].img) { blocks[j].numpar2 = (h >> 5) & PARAM2_MAX; if(r != 4) { @@ -275,8 +276,16 @@ void blocks_parse() n++; } } - if(n) blocks[j].color = 0xFF000000 | (((b/n) & 0xFF) << 16) | (((g/n) & 0xFF) << 8) | ((r/n) & 0xFF); - else blocks[j].color = 0xFFFF0000; + if(n) { + blocks[j].color = 0xFF000000 | (((b/n) & 0xFF) << 16) | (((g/n) & 0xFF) << 8) | ((r/n) & 0xFF); + while(!is_color_unique(blocks[j].color)) { + img = (unsigned char*)&blocks[j].color; + if(img[1] < 255) img[1]++; /* increase green channel first if we can */ + else if(img[0] < 255) img[0]++; + else if(img[2] < 255) img[2]++; + else img[3]--; /* for full white, decrease opacity as last resort */ + } + } else blocks[j].color = 0xFFFF0000; l++; if(!dx || !dz || (!memcmp(s, "Cobblestone", 11) && s[11] != '_' && s[11] != ' ')) { detcube(32, 32, blocks[j].img); @@ -284,7 +293,6 @@ void blocks_parse() dz = _y1 - _y0; } } - free(img); } } } diff --git a/src/main.c b/src/main.c index 4bfdd19..8855b7d 100644 --- a/src/main.c +++ b/src/main.c @@ -173,11 +173,11 @@ int main(int argc, char** argv, char** envp) if(argc < 2) { usage: printf("MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license\r\n\r\n" - "./mtsedit [-h] [-v] [-l lang] [-d|-p|-P|-b] [-m map] <.mts|.schematic> [out.mts]\r\n" + "./mtsedit [-h] [-v] [-l lang] [-d|-p|-P|-b|-B] [-m map] <.mts|.schematic> [out.mts]\r\n" "./mtsedit -g \r\n" "./mtsedit -t [blockid]\r\n" "./mtsedit -i [Minetest mods dir]\r\n" - "\r\n -v: %s\r\n -l lang: %s\r\n -d: %s\r\n -p: %s\r\n -P: %s\r\n -b: %s\r\n -m map: %s\r\n -g: %s\r\n" + "\r\n -v: %s\r\n -l lang: %s\r\n -d: %s\r\n -p: %s\r\n -P: %s\r\n -b, -B: %s\r\n -m map: %s\r\n -g: %s\r\n" " -t: %s\r\n -i: %s\r\n out.mts: %s\r\n", lang[INF_VERB], lang[INF_LANG], lang[INF_DUMP], lang[INF_PRE1], lang[INF_PRE2], lang[INF_BPRINT], lang[INF_MAP], lang[INF_GEN], lang[INF_BLK], lang[INF_MOD], lang[INF_OUT]); @@ -192,6 +192,7 @@ usage: printf("MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license case 'p': opt = 2; break; case 'P': opt = 3; break; case 'b': opt = 4; break; + case 'B': opt = 4; shift = 1; break; case 'g': opt = 5; break; case 't': opt = 6; break; case 'i': opt = 7; break; @@ -246,10 +247,10 @@ usage: printf("MineTest Schematics Editor by bzt Copyright (C) 2019 MIT license case 1: ret = mts_dump(); break; case 2: case 3: ret = mts_view(opt-2); break; - case 4: ret = mts_blueprint(); break; case 5: ret = stairgen(); break; case 6: ret = blockgen(argv[i+1]); break; - case 7: /* handle this in sdl.c, because extern does not know sizeof() */ + case 4: /* handle these in sdl.c, because extern does not know sizeof() */ + case 7: case 0: /* start the main user interface */ ret = sdlmain(opt); diff --git a/src/main.h b/src/main.h index d8f5e0b..85caa55 100644 --- a/src/main.h +++ b/src/main.h @@ -55,6 +55,8 @@ #define THEME_TABBG 8 #define THEME_SAVEACT 9 #define THEME_SAVEINACT 10 +#define THEME_BP_FG 11 +#define THEME_BP_BG 12 #define HIST_EMPTY 0 #define HIST_NODE 1 @@ -193,20 +195,23 @@ void detcube(int w, int h, unsigned char *block); int instmod(unsigned char *data, int size); int stairgen(); int blockgen(char *blockid); +int is_color_unique(uint32_t color); +int color2blockid(uint32_t color); /* blocks.c */ void blocks_getdir(char **argv, char **envp); void blocks_parse(); void blocks_free(); -/* schem.c */ +/* schemimp.c */ void schem_load(unsigned char *data, unsigned int size); +void bprint_load(unsigned char *data, unsigned int size); /* mts.c */ void mts_load(unsigned char *data, unsigned int size); int mts_save(); int mts_view(int type); -int mts_blueprint(); +int mts_blueprint(int type); int mts_dump(); int mts_getbounds(int sanitize, unsigned short *tr, unsigned short *tr2); void mts_layerprob(int diff); diff --git a/src/mts.c b/src/mts.c index 11e7e87..c0a8822 100644 --- a/src/mts.c +++ b/src/mts.c @@ -173,7 +173,7 @@ void mts_load(unsigned char *data, unsigned int size) 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 = currlayer = y+min_y; + gndlayer = y+min_y; nodes[y+min_y][z+min_z][x+min_x].param2 = 0; } status = lang[LOADED]; @@ -414,13 +414,14 @@ int mts_view(int type) /** * Export a PNG blueprint file */ -int mts_blueprint() +int mts_blueprint(int type) { - int i, w, h, x, y, z; - char pngfile[MAXPATHLEN], *c; + 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 *bprint; + SDL_Surface *oldscr = screen; + SDL_Rect rect; FILE *f; status = lang[ERR_PREVIEW]; @@ -428,35 +429,72 @@ int mts_blueprint() if(mix > max) return 1; bound_valid = 0; - /* calculate dimensions and create images */ - w = maz - miz + 3; - h = (may - miy + 1) * (max - mix + 2) + 3; - bprint = SDL_CreateRGBSurface(0, w, h, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); - memset(bprint->pixels, 0, w * h * 4); - dst = (uint32_t*)bprint->pixels; + 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(i = w, y = may; y >= miy; y--, i += w) { + 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++) - if(nodes[y][z][x].param0) - dst[i] = blocks[nodes[y][z][x].param0].color; + dst[i] = (nodes[y][z][x].param0 ? blocks[nodes[y][z][x].param0].color : 0); if(y == gndlayer) dst[i] = 0xFF0000FF; - i++; + i += w - bw - 2; } - dst[i] = y == gndlayer ? 0xFF0000FF : 0xFF000000; } /* write result to PNG file */ - buff = (unsigned char *)stbi_write_png_to_mem(bprint->pixels, bprint->pitch, bprint->w, bprint->h, 4, &i); - SDL_FreeSurface(bprint); + 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); - c = strrchr(pngfile, '.'); - if(!c) c = &pngfile[strlen(pngfile)]; - strcpy(c, "_bp.png"); + strcpy(e, "_bp.png"); f = fopen(pngfile,"wb"); if(f) { fwrite(buff, i, 1, f); diff --git a/src/schem.c b/src/schemimp.c similarity index 60% rename from src/schem.c rename to src/schemimp.c index 8f72b22..2a6473a 100644 --- a/src/schem.c +++ b/src/schemimp.c @@ -1,5 +1,5 @@ /* - * mtsedit/schem.c + * mtsedit/schemimp.c * * Copyright (C) 2019 bzt (bztsrc@gitlab) * @@ -23,26 +23,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * - * @brief Import a Minecraft NBT .schematic file + * @brief Import various non-MTS formats * */ #include "main.h" -/* parse an integer, the only type we are interested */ -#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) - /** * 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, x, y, z, min_x = 0, min_y = 0, min_z = 0; unsigned char *d, *blk, *blkd = NULL, *end = data + size; /* we don't care about that overcomplicated NBT mess */ - mts_y = mts_z = mts_x = 0; for(d = data; d < end && (!mts_y || !mts_z || !mts_x); 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 @@ -54,6 +52,7 @@ void schem_load(unsigned char *data, unsigned int size) min_x = 127 - mts_x / 2; min_y = 127 - mts_y / 2; min_z = 127 - mts_z / 2; + gndlayer = min_y; /* now that we know the dimensions, find the blocks data */ for(d = data; d < end; d++) { /* it must be type of byte array */ @@ -70,6 +69,7 @@ void schem_load(unsigned char *data, unsigned int size) for(j = 0; j < 4; j++) if(blocks[i].blockids[j] == *d) { k = i; i = numblocks; break; } if(k) { + layerprob[y+min_y] = 127; nodes[y+min_y][z+min_z][x+min_x].param0 = k; nodes[y+min_y][z+min_z][x+min_x].param1 = 127; if(blkd) @@ -83,3 +83,62 @@ void schem_load(unsigned char *data, unsigned int size) } } } + +/** + * 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] != 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-z][mts_z+min_z-z][x+min_x].param0].numref++; + blocks[0].numref--; + } + } + status = lang[LOADED]; +} diff --git a/src/sdl.c b/src/sdl.c index 8ded63b..ad97623 100644 --- a/src/sdl.c +++ b/src/sdl.c @@ -31,7 +31,7 @@ #include "data.h" uint32_t theme[] = { 0xFFF0F0F0, 0xFFF4F4F4, 0xFFBEBEBE, 0xFF808080, 0xFF5C5C5C, 0xFF4C4C4C, 0xFF454545, 0xFF383838, 0xFF303030, - 0xFFC00000, 0xFF800000 }; + 0xFFC00000, 0xFF800000, 0xFF7F7F7F, 0xFF3F0000 }; int quitting = 0, activetool = -1, overtool = -1, activeblock = 0, overblock = -1, palette[16], strmaxw, strsepar = 1; int shift = 0, ctrl = 0, help = 0, systemcursor = 0; @@ -325,7 +325,7 @@ void sdldo(int opt) case 9: hist_prepare(HIST_DELZ, cz); mts_delz(cz); hist_commit(); break; case 10: hist_prepare(HIST_ADDX, cx); mts_addx(cx, shift); hist_commit(); break; case 11: hist_prepare(HIST_DELX, cx); mts_delx(cx); hist_commit(); break; - case 12: mts_blueprint(); break; + case 12: mts_blueprint(shift); break; } SDL_SetCursor(pointer); SDL_ShowCursor(0); systemcursor = 0; @@ -345,13 +345,16 @@ int sdlmain(int opt) SDL_Event event, lookahead; /* this is tricky, because extern wouldn't know sizeof() */ - if(opt) return instmod(binary_mtsedit, sizeof(binary_mtsedit)); + if(opt == 7) return instmod(binary_mtsedit, sizeof(binary_mtsedit)); /* get our font */ font = (psf_t*)stbi_zlib_decode_malloc_guesssize_headerflag((const char*)binary_font_psf_gz + 10, sizeof(binary_font_psf_gz) - 10, 32768, &len, 0); if(!font) error(lang[ERR_MEM]); + /* as an additional dependency, blueprint needs the font */ + if(opt) return mts_blueprint(shift); + /* initialize SDL */ SDL_Init(SDL_INIT_VIDEO); fn = strrchr(mtsfile, DIRSEP); diff --git a/src/util.c b/src/util.c index 9a65374..7f85f0b 100644 --- a/src/util.c +++ b/src/util.c @@ -81,7 +81,7 @@ void readschem() if(isdir(mtsfile)) return; bound_valid = 0; - mts_x = mts_y = mts_z = 0; currlayer = 127; + mts_x = mts_y = mts_z = 0; memset(layerprob, 127, sizeof(layerprob)); memset(nodes, 0, sizeof(nodes)); for(x = 0; x < numblocks; x++) { @@ -90,7 +90,7 @@ void readschem() } blocks[0].numref = 256 * 256 * 256; savepal = savemapgen = savebiome = 0; - currlayer = cx = cz = 127; + currlayer = gndlayer = cx = cz = 127; status = lang[ERR_LOAD]; f = fopen(mtsfile, "rb"); @@ -121,7 +121,9 @@ void readschem() /* 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); + if(!memcmp(data + 3, "Schematic", 9)) schem_load(data, size); else + /* Blueprint PNG */ + if(!memcmp(data, "\x89PNG", 4)) bprint_load(data, size); /* make sure that all non-air blocks have their probability set */ for(y = 0; y < 256; y++) @@ -131,6 +133,7 @@ void readschem() if(!(nodes[y][z][x].param1 & 0x7F)) nodes[y][z][x].param1 |= 127; nodes[y][z][x].param2 &= PARAM2_MAX; } + currlayer = gndlayer; } if(data) free(data); } @@ -1254,3 +1257,39 @@ int blockgen(char *blockid) 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; +}