X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Ftools%2Fpng2map%2Fpng2map.c;h=c0188e20ac981c460fa7b781c43829c703e0aa21;hb=f177fb5c59af3153886d2ce1e8ffad33201d5d25;hp=0d2d861b99c1a2cd9db08af0ba119e72c593003f;hpb=c0b14c3f4f70e4db84495fe269ab910c7dd1429c;p=d2df-sdl.git
diff --git a/src/tools/png2map/png2map.c b/src/tools/png2map/png2map.c
index 0d2d861..c0188e2 100644
--- a/src/tools/png2map/png2map.c
+++ b/src/tools/png2map/png2map.c
@@ -1,587 +1,591 @@
-/* Copyright (C) Doom 2D: Forever Developers
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3 of the License ONLY.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include
-#include
-
-#include "mapdef.h"
-
-#define STB_IMAGE_IMPLEMENTATION
-#include "stb_image.h"
-#define STB_IMAGE_WRITE_IMPLEMENTATION
-#include "stb_image_write.h"
-
-#define PATHSIZE 1024
-#define PANEL_ALLOC_STEP 128
-
-#ifdef _WIN32
-# define f_mkdir(name) mkdir(name)
-#else
-# define f_mkdir(name) mkdir(name, 0777)
-#endif
-
-typedef struct {
- int w, h, bpp;
- uint32_t *data;
-} img_t;
-
-int g_scale = 1;
-int g_bgcell = 512;
-int g_fgcell = 512;
-int g_nooptimize = 0;
-int g_streamstexture = 1; // 0=no, 1=bg, 2=fg
-int g_liquidstexture = 3; // 0=no, 1=bg, 2=fg, 3=yes, 4=default
-
-char g_texsec[PATHSIZE] = "TEXTURES";
-char g_bgsec[PATHSIZE] = "MEGA_BG";
-char g_fgsec[PATHSIZE] = "MEGA_FG";
-char g_resdir[PATHSIZE] = "RES";
-char g_bgname[PATHSIZE] = "_BACKGROUND";
-char g_fgname[PATHSIZE] = "_FOREGROUND";
-
-// indexed with PANEL_ enum
-const char *tex_name[] = {
- NULL,
- "WALL",
- NULL,
- NULL,
- "WATER",
- "ACID1",
- "ACID2",
- "STEP",
- "STREAMUP",
- "STREAMDN",
- "DOOR",
- "DOOR",
- NULL,
- "STREAMLT",
- "STREAMRT",
-};
-
-const uint32_t map_palette[] = {
- 0x00000000,
- 0xFFD0D0D0, // WALL
- 0x00000000,
- 0x00000000,
- 0xFFC00000, // WATER
- 0xFF00B000, // ACID1
- 0xFF0000B0, // ACID2
- 0xFF808080, // STEP
- 0xFF244874, // STREAMUP
- 0xFF607C74, // STREAMDOWN
- 0xFF5CDC64, // OPENDOOR
- 0xFF40B8D4, // DOOR
- 0xFFC000C0, // BLOCKMON
- 0xFF0450C8, // STREAMLT
- 0xFF388CFC, // STREAMRT
-};
-
-map_panel_t *map_pan = NULL;
-int map_numpan = 0;
-int map_cappan = 0;
-map_texture_t *map_tex = NULL;
-int map_numtex = 0;
-
-uint16_t map_width = 1600;
-uint16_t map_height = 1600;
-
-img_t img_bg = { 0 };
-img_t img_fg = { 0 };
-img_t img_map = { 0 };
-
-/* option parsing */
-
-#define OPT_NUMSEL 8
-
-typedef enum {
- OPT_BOOL,
- OPT_INT,
- OPT_STR,
- OPT_SEL,
-} opt_type_t;
-
-typedef struct {
- opt_type_t type;
- const char *name;
- const char *sel[OPT_NUMSEL];
- union {
- char *val_str;
- int *val_int;
- };
-} opt_t;
-
-#define DEF_OPT_BOOL(oname) \
- { .type = OPT_BOOL, .name = #oname, .val_int = &g_##oname }
-#define DEF_OPT_INT(oname) \
- { .type = OPT_INT, .name = #oname, .val_int = &g_##oname }
-#define DEF_OPT_STR(oname) \
- { .type = OPT_STR, .name = #oname, .val_str = g_##oname }
-#define DEF_OPT_SEL(oname, ...) \
- { .type = OPT_SEL, .name = #oname, .sel = { __VA_ARGS__ }, .val_int = &g_##oname }
-
-const opt_t opt_defs[] = {
- DEF_OPT_INT(scale),
- DEF_OPT_INT(bgcell),
- DEF_OPT_INT(fgcell),
- DEF_OPT_BOOL(nooptimize),
- DEF_OPT_STR(texsec),
- DEF_OPT_STR(bgsec),
- DEF_OPT_STR(fgsec),
- DEF_OPT_STR(resdir),
- DEF_OPT_SEL(streamstexture, "no", "bg", "fg"),
- DEF_OPT_SEL(liquidstexture, "no", "bg", "fg", "yes", "xtra"),
-};
-
-const size_t opt_numdefs = sizeof(opt_defs) / sizeof(opt_t);
-
-const char *opt_parse(const char *str) {
- if (!str || str[0] != '-' || str[1] != '-' || !str[2])
- return "invalid option string";
-
- str += 2;
-
- char *eq = strrchr(str, '=');
- if (eq) *eq = 0;
- const char *opts = str;
- const char *vals = eq ? eq + 1 : NULL;
-
- for (int i = 0; i < opt_numdefs; ++i) {
- if (strcmp(opts, opt_defs[i].name))
- continue;
-
- switch (opt_defs[i].type) {
- case OPT_BOOL:
- if (eq) return "don't need an explicit value";
- *opt_defs[i].val_int = 1;
- return NULL;
- case OPT_INT:
- if (!eq) return "need numeric value";
- *opt_defs[i].val_int = atoi(vals);
- return NULL;
- case OPT_STR:
- if (!eq) return "need string value";
- strncpy(opt_defs[i].val_str, vals, PATHSIZE);
- opt_defs[i].val_str[PATHSIZE-1] = 0;
- return NULL;
- case OPT_SEL:
- if (!eq) return "need string value";
- for (int j = 0; j < OPT_NUMSEL && opt_defs[i].sel[j]; ++j) {
- if (!strcmp(opt_defs[i].sel[j], vals)) {
- *opt_defs[i].val_int = j;
- return NULL;
- }
- }
- return "invalid variant value";
- default:
- break;
- }
- }
-
- return "unknown option";
-}
-
-/* -------------- */
-
-/* map writing */
-
-int map_save(const char *fname) {
- FILE *fout = fopen(fname, "wb");
- if (!fout) return -1;
-
- map_header_t hdr = { { 0 } };
- strcpy(hdr.name, "png2map map");
- strcpy(hdr.author, "png2map");
- strcpy(hdr.desc, "generated by png2map");
- hdr.music[0] = 0;
- hdr.sky[0] = 0;
- hdr.width = map_width;
- hdr.height = map_height;
-
- map_block_t blk = { MBLK_HEADER, 0, sizeof(hdr) };
-
- fwrite(MAP_MAGIC, 4, 1, fout);
- fwrite(&blk, sizeof(blk), 1, fout);
- fwrite(&hdr, sizeof(hdr), 1, fout);
-
- blk.type = MBLK_TEXTURES;
- blk.size = sizeof(map_texture_t) * map_numtex;
- fwrite(&blk, sizeof(blk), 1, fout);
- for (int i = 0; i < map_numtex; ++i)
- fwrite(map_tex + i, sizeof(map_texture_t), 1, fout);
-
- blk.type = MBLK_PANELS;
- blk.size = sizeof(map_panel_t) * map_numpan;
- fwrite(&blk, sizeof(blk), 1, fout);
- for (int i = 0; i < map_numpan; ++i)
- fwrite(map_pan + i, sizeof(map_panel_t), 1, fout);
-
- blk.type = MBLK_NONE;
- blk.size = 0;
- fwrite(&blk, sizeof(blk), 1, fout);
-
- return 0;
-}
-
-int map_cachetex(const char *name) {
- static char path[PATHSIZE];
-
- if (!name || !name[0])
- return -1;
-
- if (!strncmp(name, "_water_", 7))
- snprintf(path, PATHSIZE, name);
- else if (strchr(name, '\\'))
- snprintf(path, PATHSIZE, ":\\%s.PNG", name);
- else
- snprintf(path, PATHSIZE, ":\\%s\\%s.PNG", g_texsec, name);
-
- for (int i = 0; i < map_numtex; ++i)
- if (!strncmp(path, map_tex[i].resname, 64))
- return i;
-
- int i = map_numtex++;
- map_tex = realloc(map_tex, map_numtex * sizeof(map_texture_t));
- if (!map_tex) return -1;
-
- map_tex[i].anim = 0;
- strncpy(map_tex[i].resname, path, 64);
- map_tex[i].resname[63] = 0;
-
- return i;
-}
-
-int map_addpanel(int type, const char *tex, int x, int y, int w, int h) {
- if (!type) return -2;
-
- if (map_numpan >= map_cappan) {
- map_cappan += PANEL_ALLOC_STEP;
- map_pan = realloc(map_pan, map_cappan * sizeof(map_panel_t));
- if (!map_pan) return -1;
- }
-
- int texid = map_cachetex(tex);
- int i = map_numpan++;
- memset(map_pan + i, 0, sizeof(map_panel_t));
- map_pan[i].type = 1 << (type - 1);
- if (texid >= 0)
- map_pan[i].texid = texid;
- else
- map_pan[i].flags = PFLAG_HIDE;
- if (type >= PANEL_WATER && type <= PANEL_ACID2)
- map_pan[i].flags |= PFLAG_WATERTEXTURES;
- map_pan[i].x = x;
- map_pan[i].y = y;
- map_pan[i].w = w;
- map_pan[i].h = h;
-
- return i;
-}
-
-/* ----------- */
-
-/* image shit */
-
-int img_load(const char *fname, img_t *out) {
- out->data = (uint32_t *)stbi_load(fname, &out->w, &out->h, &out->bpp, 4);
- return !out->data;
-}
-
-void img_free(img_t *img) {
- free(img->data);
- img->data = NULL;
-}
-
-img_t *img_segment(img_t *src, int cell, int *out_cx, int *out_cy) {
- if (!src || !src->data || cell <= 0) return NULL;
-
- int cx = (src->w + cell - 1) / cell;
- int cy = (src->h + cell - 1) / cell;
- int cn = cx * cy;
-
- img_t *cells = calloc(cn, sizeof(img_t));
- if (!cells) return NULL;
- for (int i = 0; i < cn; ++i) {
- cells[i].w = cells[i].h = cell;
- cells[i].data = calloc(cell * cell, 4);
- if (!cells[i].data) {
- free(cells);
- return NULL;
- }
- }
-
- // this is awfully retarded, but who cares
- uint32_t *inp = src->data;
- for (int y = 0; y < src->h; ++y) {
- int cell_y = y / cell;
- for (int x = 0; x < src->w; ++x, ++inp) {
- int pos_y = y % cell;
- int pos_x = x % cell;
- int pos = pos_y * cell + pos_x;
- cells[cell_y * cx + x / cell].data[pos] = *inp;
- }
- }
-
- *out_cx = cx;
- *out_cy = cy;
- return cells;
-}
-
-int img_save(const char *fname, img_t *src) {
- static char path[PATHSIZE];
- if (!src || !src->data) return -1;
- if (!strstr(fname, ".png"))
- snprintf(path, sizeof(path), "%s.png", fname);
- else
- snprintf(path, sizeof(path), fname);
- return !stbi_write_png(path, src->w, src->h, 4, src->data, 4 * src->w);
-}
-
-/* ---------- */
-
-void die(const char *fmt, ...) {
- fprintf(stderr, "FATAL ERROR: ");
- va_list args;
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- fprintf(stderr, "\n");
-
- if (img_bg.data) img_free(&img_bg);
- if (img_fg.data) img_free(&img_fg);
- if (img_map.data) img_free(&img_map);
- if (map_pan) free(map_pan);
- if (map_tex) free(map_tex);
-
- exit(1);
-}
-
-void tex_check(const char *resdir, int liquidsmode) {
- static const char *liquids[] = { "_water_0", "_water_1", "_water_2" };
- char path[PATHSIZE] = { 0 };
-
- if (!liquidsmode) {
- tex_name[PANEL_WATER] = tex_name[PANEL_ACID1] = tex_name[PANEL_ACID2] = NULL;
- } else if (liquidsmode == 4) {
- tex_name[PANEL_WATER] = liquids[0];
- tex_name[PANEL_ACID1] = liquids[1];
- tex_name[PANEL_ACID2] = liquids[2];
- }
-
- for (int i = 0; i < PANEL_NUMTYPES; ++i) {
- if (!tex_name[i]) continue;
- snprintf(path, PATHSIZE, "%s/%s.png", resdir, tex_name[i]);
- FILE *f = fopen(path, "rb");
- if (f) {
- fclose(f);
- } else {
- printf("texture `%s` missing from `%s`, using default\n", tex_name[i], resdir);
- if (i >= PANEL_WATER && i <= PANEL_ACID2 && liquidsmode)
- tex_name[i] = liquids[i - PANEL_WATER];
- else
- tex_name[i] = NULL;
- }
- }
-}
-
-void scronch_layer(int layer, img_t *img, int cellsize, const char *secname) {
- int numx = 1;
- int numy = 1;
- img_t *cells = cellsize ? img_segment(img, cellsize, &numx, &numy) : img;
-
- if (!numx || !numy || !cells)
- die("scronching failure");
-
- f_mkdir(secname);
-
- int pw, ph;
- if (cellsize) {
- pw = ph = cellsize;
- } else {
- pw = img->w;
- ph = img->h;
- }
-
- char path[PATHSIZE] = { 0 };
- for (int yc = 0; yc < numy; ++yc) {
- int y = yc * cellsize;
- for (int xc = 0; xc < numx; ++xc) {
- int x = xc * cellsize;
- int idx = yc * numx + xc;
- snprintf(path, PATHSIZE, "%s\\BG%03X%03X", secname, xc, yc);
- if (img_save(path, cells + idx))
- die("could not save layer cell `%s`", path);
- if (cells != img)
- img_free(cells + idx);
- if (map_addpanel(layer, path, x, y, pw, ph) < 0)
- die("could not add layer panel");
- }
- }
-
- if (cells != img)
- free(cells);
-}
-
-static inline uint32_t type_for_color(const uint32_t c) {
- if (!c) return 0; // empty
- for (int i = 0; i < PANEL_NUMTYPES; ++i)
- if (map_palette[i] == c)
- return i;
- return 0;
-}
-
-static inline int spawn_panel(uint32_t type, int x, int y, int w, int h) {
- const char *tex = tex_name[type];
-
- switch (type) {
- case PANEL_WATER:
- case PANEL_ACID1:
- case PANEL_ACID2:
- if (g_liquidstexture == 1 || g_liquidstexture == 2) {
- map_addpanel(PANEL_BACK + g_liquidstexture - 1, tex, x, y, w, h);
- tex = NULL;
- }
- break;
- case PANEL_LIFTUP:
- case PANEL_LIFTDOWN:
- case PANEL_LIFTLEFT:
- case PANEL_LIFTRIGHT:
- if (g_streamstexture == 1 || g_streamstexture == 2) {
- map_addpanel(PANEL_BACK + g_streamstexture - 1, tex, x, y, w, h);
- tex = NULL;
- }
- break;
- default:
- break;
- }
-
- map_addpanel(type, tex, x, y, w, h);
-}
-
-static inline void fill_panel_opt(const img_t *img, uint8_t *done, int x1, int y1, int scale) {
- #define PIXEQ(xx, yy) (img->data[(yy) * w + (xx)] == c1)
- #define DONE(xx, yy) (done[(yy) * w + (xx)])
- const uint32_t c1 = img->data[y1 * img->w + x1];
- const uint32_t type = type_for_color(c1);
- if (!type) {
- done[y1 * img->w + x1] = 1;
- return; // unknown panel or emptiness
- }
-
- const int w = img->w;
- const int h = img->h;
-
- // this ain't optimal, but it'll do
-
- // find horizontal bounds
- int ml = x1, mr = x1;
- for (; ml > 0 && !DONE(ml-1, y1) && PIXEQ(ml-1, y1); --ml);
- for (; mr < w-1 && !DONE(mr+1, y1) && PIXEQ(mr+1, y1); ++mr);
-
- // find min vertical bounds
- int mu = 0, md = h - 1;
- for (int x = ml; x <= mr; ++x) {
- int tu, td;
- for (tu = y1; tu > 0 && !DONE(x, tu-1) && PIXEQ(x, tu-1); --tu);
- for (td = y1; td < h-1 && !DONE(x, td+1) && PIXEQ(x, td+1); ++td);
- if (tu > mu) mu = tu;
- if (td < md) md = td;
- }
-
- // don't overlap this later
- for (int y = mu; y <= md; ++y)
- for (int x = ml; x <= mr; ++x)
- DONE(x, y) = 1;
-
- int pw = (mr - ml + 1);
- int ph = (md - mu + 1);
- if (spawn_panel(type, ml * scale, mu * scale, scale * pw, scale * ph) < 0)
- die("could not add panel at (%d, %d) x (%d, %d)", ml, mu, pw, ph);
- #undef PIX
- #undef DONE
-}
-
-static inline void fill_panel_single(const img_t *img, uint8_t *done, int x1, int y1, int scale) {
- const uint32_t c1 = img->data[y1 * img->w + x1];
- const uint32_t type = type_for_color(c1);
- done[y1 * img->w + x1] = 1;
- if (!type) return; // unknown panel or emptiness
- spawn_panel(type, x1 * scale, y1 * scale, scale, scale);
-}
-
-static inline void fill_panel(const img_t *img, uint8_t *done, int x1, int y1, int scale) {
- if (g_nooptimize)
- fill_panel_single(img, done, x1, y1, scale);
- else
- fill_panel_opt(img, done, x1, y1, scale);
-}
-
-void convert_map(img_t *img, int scale) {
- if (!img || !img->data) return;
-
- uint8_t *done = calloc(1, img->w * img->h);
-
- for (int x = 0; x < img->w; ++x) {
- for (int y = 0; y < img->h; ++y) {
- if (done[y * img->w + x]) continue;
- fill_panel(img, done, x, y, scale);
- }
- }
-
- map_width = img->w * scale;
- map_height = img->h * scale;
-}
-
-int main(int argc, const char **argv) {
- if (argc < 3) {
- printf("usage: %s