From 96b65ba63cd0fd7a74926013755e370117ae3658 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 2 Nov 2004 05:07:00 +0000 Subject: [PATCH] get wad creation mostly working (known to work for map textures) This is an imperfect revision of history. --- tools/wad/Makefile.am | 2 +- tools/wad/grab.c | 494 ++++++++++++++++++++++++++++++++++++++++++ tools/wad/script.c | 262 ++++++++++++++++++++++ tools/wad/wad.c | 19 +- tools/wad/wad.h | 15 ++ 5 files changed, 775 insertions(+), 17 deletions(-) create mode 100644 tools/wad/grab.c create mode 100644 tools/wad/script.c diff --git a/tools/wad/Makefile.am b/tools/wad/Makefile.am index 3b4134b48..e951fb687 100644 --- a/tools/wad/Makefile.am +++ b/tools/wad/Makefile.am @@ -19,7 +19,7 @@ EXTRA_PROGRAMS= wad man_MANS= $(mans) -wad_SOURCES= wad.c +wad_SOURCES= grab.c script.c wad.c wad_LDADD= $(WAD_LIBS) wad_DEPENDENCIES= $(WAD_DEPS) diff --git a/tools/wad/grab.c b/tools/wad/grab.c new file mode 100644 index 000000000..91efc2b16 --- /dev/null +++ b/tools/wad/grab.c @@ -0,0 +1,494 @@ +/* Copyright (C) 1996-1997 Id Software, Inc. + + 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; either version 2 of the License, or + (at your option) any later version. + + 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + See file, 'COPYING', for details. +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static __attribute__ ((unused)) const char rcsid[] = + "$Id$"; + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/dstring.h" +#include "QF/image.h" +#include "QF/qendian.h" +#include "QF/script.h" +#include "QF/sys.h" +#include "QF/wad.h" + +#include "wad.h" + +typedef struct { + int width, height; + int widthbits, heightbits; + unsigned char data[4]; +} qtex_t; + +#define SCRN(x,y) (*(image->data+(y)*image->width*4+x)) + +#if 0 +/* + GrabRaw + + filename RAW x y width height +*/ +void +GrabRaw (script_t *script) +{ + int x, y, xl, yl, xh, yh, w, h; + byte *screen_p; + int linedelta; + + Script_GetToken (script, false); + xl = atoi (script->token->str); + Script_GetToken (script, false); + yl = atoi (script->token->str); + Script_GetToken (script, false); + w = atoi (script->token->str); + Script_GetToken (script, false); + h = atoi (script->token->str); + + xh = xl + w; + yh = yl + h; + + screen_p = byteimage + yl * byteimagewidth + xl; + linedelta = byteimagewidth - w; + + for (y = yl; y < yh; y++) { + for (x = xl; x < xh; x++) { + *lump_p++ = *screen_p; + *screen_p++ = 0; + } + screen_p += linedelta; + } +} +#endif + + +/* + GrabPalette + + filename PALETTE [startcolor endcolor] +*/ +void +GrabPalette (script_t *script) +{ + int start, end, length; + + if (Script_TokenAvailable (script, false)) { + Script_GetToken (script, false); + start = atoi (script->token->str); + Script_GetToken (script, false); + end = atoi (script->token->str); + } else { + start = 0; + end = 255; + } + + length = 3 * (end - start + 1); + lumpbuffer = lump_p = malloc (length); + memcpy (lump_p, default_palette + start * 3, length); + lump_p += length; +} + +#if 0 +/* + GrabPic + + filename qpic x y width height +*/ +void +GrabPic (script_t *script) +{ + int x, y, xl, yl, xh, yh; + int width; + byte transcolor; + qpic_t *header; + + Script_GetToken (script, false); + xl = atoi (script->token->str); + Script_GetToken (script, false); + yl = atoi (script->token->str); + Script_GetToken (script, false); + xh = xl - 1 + atoi (script->token->str); + Script_GetToken (script, false); + yh = yl - 1 + atoi (script->token->str); + + if (xh < xl || yh < yl || xl < 0 || yl < 0 || xh > 319 || yh > 199) + Sys_Error ("GrabPic: Bad size: %i, %i, %i, %i", xl, yl, xh, yh); + + transcolor = 255; + + + // fill in header + header = (qpic_t *) lump_p; + width = xh - xl + 1; + header->width = LittleLong (width); + header->height = LittleLong (yh - yl + 1); + + // start grabbing posts + lump_p = (byte *) header->data; + + for (y = yl; y <= yh; y++) + for (x = xl; x <= xh; x++) + *lump_p++ = SCRN (x, y); +} +#endif + +/* COLORMAP GRABBING */ + +static byte +BestColor (int r, int g, int b, int start, int stop) +{ + int i; + int dr, dg, db; + int bestdistortion, distortion; + int bestcolor; + byte *pal; + + // let any color go to 0 as a last resort + bestdistortion = ((int) r * r + (int) g * g + (int) b * b) * 2; + bestcolor = 0; + + pal = default_palette + start * 3; + for (i = start; i <= stop; i++) { + dr = r - (int) pal[0]; + dg = g - (int) pal[1]; + db = b - (int) pal[2]; + pal += 3; + distortion = dr * dr + dg * dg + db * db; + if (distortion < bestdistortion) { + if (!distortion) + return i; // perfect match + + bestdistortion = distortion; + bestcolor = i; + } + } + + return bestcolor; +} + +#if 0 + +/* + GrabColormap + + filename COLORMAP levels fullbrights + + the first map is an identiy 0-255 + the final map is all black except for the fullbrights + the remaining maps are evenly spread + fullbright colors start at the top of the palette. +*/ +void +GrabColormap (script_t *script) +{ + int levels, brights; + int l, c; + float frac, red, green, blue; + + Script_GetToken (script, false); + levels = atoi (script->token->str); + Script_GetToken (script, false); + brights = atoi (script->token->str); + + // identity lump + for (l = 0; l < 256; l++) + *lump_p++ = l; + + // shaded levels + for (l = 1; l < levels; l++) { + frac = 1.0 - (float) l / (levels - 1); + for (c = 0; c < 256 - brights; c++) { + red = lbmpalette[c * 3]; + green = lbmpalette[c * 3 + 1]; + blue = lbmpalette[c * 3 + 2]; + + red = (int) (red * frac + 0.5); + green = (int) (green * frac + 0.5); + blue = (int) (blue * frac + 0.5); + + // note: 254 instead of 255 because 255 is the transparent color, + // and we don't want anything remapping to that + *lump_p++ = BestColor (red, green, blue, 0, 254); + } + for (; c < 256; c++) + *lump_p++ = c; + } + + *lump_p++ = brights; +} + +/* + GrabColormap2 + + experimental -- not used by quake + + filename COLORMAP2 range levels fullbrights + + fullbright colors start at the top of the palette. + Range can be greater than 1 to allow overbright color tables. + + the first map is all 0 + the last (levels-1) map is at range +*/ +void +GrabColormap2 (script_t *script) +{ + int levels, brights; + int l, c; + float frac, red, green, blue; + float range; + + Script_GetToken (script, false); + range = atof (script->token->str); + Script_GetToken (script, false); + levels = atoi (script->token->str); + Script_GetToken (script, false); + brights = atoi (script->token->str); + + // shaded levels + for (l = 0; l < levels; l++) { + frac = range - range * (float) l / (levels - 1); + for (c = 0; c < 256 - brights; c++) { + red = lbmpalette[c * 3]; + green = lbmpalette[c * 3 + 1]; + blue = lbmpalette[c * 3 + 2]; + + red = (int) (red * frac + 0.5); + green = (int) (green * frac + 0.5); + blue = (int) (blue * frac + 0.5); + + // note: 254 instead of 255 because 255 is the transparent color, + // and we don't want anything remapping to that + *lump_p++ = BestColor (red, green, blue, 0, 254); + } + + // fullbrights allways stay the same + for (; c < 256; c++) + *lump_p++ = c; + } + + *lump_p++ = brights; +} +#endif + +/* MIPTEX GRABBING */ + +typedef struct { + char name[16]; + unsigned width, height; + unsigned offsets[4]; // four mip maps stored +} miptex_t; +#if 0 +byte pixdata[256]; + +int d_red, d_green, d_blue; + +static byte +AveragePixels (int count) +{ + int r, g, b; + int i; + int vis; + int pix; + int dr, dg, db; + int bestdistortion, distortion; + int bestcolor; + byte *pal; + int fullbright; + int e; + + vis = 0; + r = g = b = 0; + fullbright = 0; + for (i = 0; i < count; i++) { + pix = pixdata[i]; + if (pix == 255) + fullbright = 2; + else if (pix >= 240) { + return pix; + if (!fullbright) { + fullbright = true; + r = 0; + g = 0; + b = 0; + } + } else { + if (fullbright) + continue; + } + + r += default_palette[pix * 3]; + g += default_palette[pix * 3 + 1]; + b += default_palette[pix * 3 + 2]; + vis++; + } + + if (fullbright == 2) + return 255; + + r /= vis; + g /= vis; + b /= vis; + + if (!fullbright) { + r += d_red; + g += d_green; + b += d_blue; + } + // find the best color + bestdistortion = r * r + g * g + b * b; + bestcolor = 0; + if (fullbright) { + i = 240; + e = 255; + } else { + i = 0; + e = 240; + } + + for (; i < e; i++) { + pix = i; // pixdata[i]; + + pal = default_palette + pix * 3; + + dr = r - (int) pal[0]; + dg = g - (int) pal[1]; + db = b - (int) pal[2]; + + distortion = dr * dr + dg * dg + db * db; + if (distortion < bestdistortion) { + if (!distortion) { + d_red = d_green = d_blue = 0; // no distortion yet + return pix; // perfect match + } + + bestdistortion = distortion; + bestcolor = pix; + } + } + + if (!fullbright) { // error diffusion + pal = default_palette + bestcolor * 3; + d_red = r - (int) pal[0]; + d_green = g - (int) pal[1]; + d_blue = b - (int) pal[2]; + } + + return bestcolor; +} +#endif + + +/* + GrabMip + + filename MIP x y width height + must be multiples of sixteen +*/ +void +GrabMip (script_t *script) +{ + int x, y, xl, yl, xh, yh, w, h; + byte *screen_p, *source; + int linedelta; + miptex_t *qtex; + int miplevel, mipstep; + int xx, yy; + int count; + int r, g, b; + + Script_GetToken (script, false); + xl = atoi (script->token->str); + Script_GetToken (script, false); + yl = atoi (script->token->str); + Script_GetToken (script, false); + w = atoi (script->token->str); + Script_GetToken (script, false); + h = atoi (script->token->str); + + if ((w & 15) || (h & 15)) + Sys_Error ("line %i: miptex sizes must be multiples of 16", + script->line); + + xh = xl + w; + yh = yl + h; + + qtex = malloc (sizeof (miptex_t) + w * h / 64 * 85); + qtex->width = LittleLong (w); + qtex->height = LittleLong (h); + strcpy (qtex->name, lumpname->str); + + lumpbuffer = (byte *) qtex; + lump_p = (byte *) &qtex->offsets[4]; + + screen_p = image->data + yl * image->width * 4 + xl; + linedelta = (image->width - w) * 4; + + source = image->data; + qtex->offsets[0] = LittleLong (lump_p - (byte *) qtex); + + for (y = yl; y < yh; y++) { + for (x = xl; x < xh; x++) { + r = *screen_p++; + g = *screen_p++; + b = *screen_p++; + *screen_p++; // skip over alpha + *lump_p++ = BestColor (r, g, b, 0, 239); + } + screen_p += linedelta; + } + + // subsample for greater mip levels + //d_red = d_green = d_blue = 0; // no distortion yet + + for (miplevel = 1; miplevel < 4; miplevel++) { + qtex->offsets[miplevel] = LittleLong (lump_p - (byte *) qtex); + + mipstep = 1 << miplevel; + for (y = 0; y < h; y += mipstep) { + + for (x = 0; x < w; x += mipstep) { + count = 0; + r = g = b = 0; + for (yy = 0; yy < mipstep; yy++) + for (xx = 0; xx < mipstep; xx++) { + r += source [((y + yy) * w + x + xx) * 4 + 0]; + g += source [((y + yy) * w + x + xx) * 4 + 1]; + b += source [((y + yy) * w + x + xx) * 4 + 2]; + count++; + } + r /= count; + g /= count; + b /= count; + *lump_p++ = BestColor (r, g, b, 0, 239); + } + } + } +} diff --git a/tools/wad/script.c b/tools/wad/script.c new file mode 100644 index 000000000..ce86ab690 --- /dev/null +++ b/tools/wad/script.c @@ -0,0 +1,262 @@ +/* + #FILENAME# + + #DESCRIPTION# + + Copyright (C) 2004 #AUTHOR# + + Author: #AUTHOR# + Date: 2004/9/28 + + 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; either version 2 + of the License, or (at your option) any later version. + + 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, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +static __attribute__ ((unused)) const char rcsid[] = + "$Id$"; + +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/GL/defines.h" +#include "QF/dstring.h" +#include "QF/image.h" +#include "QF/png.h" +#include "QF/script.h" +#include "QF/sys.h" +#include "QF/wad.h" +#include "QF/va.h" + +#include "wad.h" + +static dstring_t destfile = {&dstring_default_mem}; +static qboolean savesingle = false; +static wad_t *wadfile; +dstring_t *lumpname; +tex_t *image; +byte *lumpbuffer, *lump_p; + +typedef struct { + const char *name; + void (*func)(script_t *script); +} command_t; + +static void +unimp (script_t *script) +{ + Sys_Error ("unimplemented: %d", script->line); +} + +command_t commands[] = { + {"palette", GrabPalette}, + {"colormap", unimp},//GrabColormap}, + {"qpic", unimp},//GrabPic}, + {"--", unimp}, + {"miptex", GrabMip}, + {"raw", unimp},//GrabRaw}, + + {"colormap2", unimp},//GrabColormap2}, + + {0, 0}, +}; + +static void +load_image (const char *name) +{ + QFile *file; + tex_t *tex; + int i, pixels; + byte *s, *d; + + if (!(file = Qopen (name, "rb"))) + Sys_Error ("couldn't open %s. %s", name, strerror(errno)); + if (!(tex = LoadPNG (file))) + Sys_Error ("couldn't read %s as PNG", name); + + pixels = tex->width * tex->height; + image = malloc (pixels * 4 + sizeof (tex_t)); + image->width = tex->width; + image->height = tex->height; + image->format = tex_rgba; + image->palette = 0; + switch (tex->format) { + case tex_palette: + for (i = 0, s = tex->data, d = image->data; i < pixels; i++) { + byte *v = tex->palette + *s++ * 3; + *d++ = *v++; + *d++ = *v++; + *d++ = *v++; + *d++ = 255; + } + break; + case tex_l: + for (i = 0, s = tex->data, d = image->data; i < pixels; i++) { + byte l = *s++; + *d++ = l; + *d++ = l; + *d++ = l; + *d++ = 255; + } + break; + case tex_a: + for (i = 0, s = tex->data, d = image->data; i < pixels; i++) { + *d++ = 255; + *d++ = 255; + *d++ = 255; + *d++ = *s++; + } + break; + case tex_la: + for (i = 0, s = tex->data, d = image->data; i < pixels; i++) { + byte l = *s++; + *d++ = l; + *d++ = l; + *d++ = l; + *d++ = *s++; + } + break; + case tex_rgb: + for (i = 0, s = tex->data, d = image->data; i < pixels; i++) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = 255; + } + break; + case tex_rgba: + memcpy (image->data, tex->data, pixels * 4); + break; + default: + Sys_Error ("unknown texture type"); + } +} + +static void +write_file (void) +{ + QFile *file; + const char *name; + + name = va ("%s/%s.lmp", destfile.str, lumpname->str); + if (!(file = Qopen (name, "wb"))) + Sys_Error ("couldn't open %s. %s", name, strerror(errno)); + Qwrite (file, lumpbuffer, lump_p - lumpbuffer); + Qclose (file); + free (lumpbuffer); + lumpbuffer = lump_p = 0; +} + +static void +write_lump (int type) +{ + if (!wadfile) { + wadfile = wad_create (destfile.str); + if (!wadfile) + Sys_Error ("couldn't create %s. %s", destfile.str, + strerror(errno)); + } + wad_add_data (wadfile, lumpname->str, type, lumpbuffer, + lump_p - lumpbuffer); + free (lumpbuffer); + lumpbuffer = lump_p = 0; +} + +static void +parse_script (script_t *script) +{ + int cmd; + + while (Script_GetToken (script, true)) { + if (strcasecmp ("$LOAD", script->token->str) == 0) { + Script_GetToken (script, false); + load_image (script->token->str); + continue; + } + if (strcasecmp ("$DEST", script->token->str) == 0) { + Script_GetToken (script, false); + dstring_copystr (&destfile, script->token->str); + continue; + } + if (strcasecmp ("$SINGLEDEST", script->token->str) == 0) { + Script_GetToken (script, false); + dstring_copystr (&destfile, script->token->str); + savesingle = true; + continue; + } + + if (!lumpname) + lumpname = dstring_newstr (); + dstring_copystr (lumpname, script->token->str); + + Script_GetToken (script, false); + for (cmd = 0; commands[cmd].name; cmd++) { + if (!strcasecmp (script->token->str, commands[cmd].name)) { + commands[cmd].func (script); + break; + } + } + if (!commands[cmd].name) + Sys_Error ("Unrecognized token '%s' at line %i", script->token->str, + script->line); + //grabbed++; + + if (savesingle) + write_file (); + else + write_lump (TYP_LUMPY + cmd); + } +} + +void +process_wad_script (const char *name) +{ + char *buf; + QFile *file; + int bytes; + script_t *script; + + file = Qopen (name, "rt"); + if (!file) + Sys_Error ("couldn't open %s. %s", name, strerror(errno)); + bytes = Qfilesize (file); + buf = malloc (bytes + 1); + bytes = Qread (file, buf, bytes); + buf[bytes] = 0; + Qclose (file); + + dstring_copystr (&destfile, name); + dstring_appendstr (&destfile, ".wad"); + + script = Script_New (); + Script_Start (script, name, buf); + + parse_script (script); + + if (wadfile) + wad_close (wadfile); +} diff --git a/tools/wad/wad.c b/tools/wad/wad.c index bf305a837..5611c4881 100644 --- a/tools/wad/wad.c +++ b/tools/wad/wad.c @@ -157,7 +157,7 @@ decode_args (int argc, char **argv) return optind; } -static byte default_palette[] = { +byte default_palette[] = { 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x1F, 0x1F, 0x1F, 0x2F, 0x2F, 0x2F, 0x3F, 0x3F, 0x3F, 0x4B, 0x4B, 0x4B, 0x5B, 0x5B, 0x5B, 0x6B, 0x6B, 0x6B, 0x7B, 0x7B, 0x7B, 0x8B, 0x8B, 0x8B, 0x9B, 0x9B, 0x9B, 0xAB, 0xAB, 0xAB, @@ -339,7 +339,7 @@ main (int argc, char **argv) decode_args (argc, argv); if (!options.wadfile) { - fprintf (stderr, "%s: no archive file specified.\n", + fprintf (stderr, "%s: no archive/script file specified.\n", this_program); usage (1); } @@ -399,20 +399,7 @@ main (int argc, char **argv) wad_close (wad); break; case mo_create: -/* wad = wad_create (options.wadfile); - if (!wad) { - fprintf (stderr, "%s: error creating %s: %s\n", this_program, - options.wadfile, - strerror (errno)); - return 1; - } - wad->pad = options.pad; - while (optind < argc) { - if (options.verbosity > 0) - printf ("%s\n", argv[optind]); - wad_add (wad, argv[optind++]); - } - wad_close (wad);*/ + process_wad_script (options.wadfile); break; default: fprintf (stderr, "%s: No command given.\n", diff --git a/tools/wad/wad.h b/tools/wad/wad.h index e3e856e75..7b5d4c054 100644 --- a/tools/wad/wad.h +++ b/tools/wad/wad.h @@ -52,4 +52,19 @@ typedef struct { char *wadfile; // wad file to read/write/test } options_t; +extern struct tex_s *image; +extern byte default_palette[]; +extern byte *lumpbuffer, *lump_p; +extern struct dstring_s *lumpname; + +struct script_s; +void GrabPalette (struct script_s *script); +void GrabColormap (struct script_s *script); +void GrabPic (struct script_s *script); +void GrabMip (struct script_s *script); +void GrabRaw (struct script_s *script); +void GrabColormap2 (struct script_s *script); + +void process_wad_script (const char *name); + #endif // __wad_h