/* lmp.c lump file tool Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2002 Bill Currie Copyright (C) 2002,2011 Jeff Teunissen 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 */ /* read 4 bytes -> width read 4 bytes -> height width * height bytes no attached palette */ #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lmp.h" #define MEMSIZE (8 * 1024 * 1024) const char *this_program; options_t options; Pixel *palette; static const struct option long_options[] = { {"export", no_argument, 0, 'e'}, {"import", no_argument, 0, 'i'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, {"geometry", required_argument, 0, 'g'}, {"geometry", required_argument, 0, 'g'}, {"palette", required_argument, 0, 'p'}, {"raw", no_argument, 0, 'r'}, {"quiet", no_argument, 0, 'q'}, {"verbose", no_argument, 0, 'v'}, {NULL, 0, NULL, 0}, }; static char * replaceExtension (const char *oldstr, const char *extension) { char *tmp = strdup (oldstr); char *blank = strrchr (tmp, '.'); char *newstr; *blank = 0; newstr = nva ("%s.%s", tmp, extension); free (tmp); return newstr; } static void usage (int status) { Sys_Printf ("%s - QuakeForge Lump tool\n", this_program); Sys_Printf ("Usage: %s [options] [FILE...]\n", this_program); Sys_Printf ("Commands:\n" " -e, --export Export lump to PCX\n" " -i, --import Import lump from PCX\n" " -h, --help Display this help and exit\n" " -V, --version Output version information and exit\n\n"); Sys_Printf ("Options:\n" " -g, --geometry WIDTHxHEIGHT Ignore geometry for image data\n" " (Warning: does not check validity of input)\n" " -p, --palette FILE Use palette FILE for conversion\n" " -r, --raw File(s) are raw (no lump header)\n" " (must use --geometry to be useful)\n" " -q, --quiet Inhibit usual output\n" " -v, --verbose Display more output than usual\n"); exit (status); } static qboolean exportFile (const char *inpath) { char *outpath = replaceExtension (inpath, "pcx"); QFile *infile = Qopen (inpath, "rb"); QFile *outfile = Qopen (outpath, "wb"); int fsize = Qfilesize (infile); pcx_t *pcx; int pcx_size; int32_t width; int32_t height; void *data; qboolean ret = false; if (options.verbosity > 1) Sys_Printf ("file size: %d\n", fsize); if (options.raw) { width = options.width; height = options.height; if (width <= 0 || height <= 0) { Sys_Printf ("%s: cowardly refusing to write a raw image with no geometry\n", this_program); goto die; } } else { Qread (infile, &width, sizeof (width)); Qread (infile, &height, sizeof (height)); fsize -= 8; if (options.width > 0 && options.height > 0) { width = options.width; height = options.height; } else { width = LittleLong (width); height = LittleLong (height); } } if (options.verbosity > 1) Sys_Printf ("dimensions: %dx%d\n", width, height); if (options.verbosity > 1) Sys_Printf ("data size: %d\n", fsize); if ((width * height) > fsize) { Sys_Printf ("%s: %s: not enough image data for a %dx%d image.\n", this_program, inpath, width, height); goto die; } data = malloc (fsize); Qread (infile, data, fsize); pcx = EncodePCX (data, width, height, width, (byte *) palette, false, &pcx_size); free (data); if (options.verbosity > 1) Sys_Printf ("PCX data size: %d\n", pcx_size); if (Qwrite (outfile, pcx, pcx_size) != pcx_size) { Sys_Printf ("%s: Error writing to %s\n", this_program, outpath); goto die; } ret = true; die: if (outpath) free (outpath); Qclose (infile); Qclose (outfile); return ret; } static qboolean importFile (const char *inpath) { char *outpath = replaceExtension (inpath, "lmp"); QFile *infile = Qopen (inpath, "rb"); QFile *outfile = Qopen (outpath, "wb"); int fsize = Qfilesize (infile); tex_t *lmp; qboolean ret = false; if (options.verbosity > 1) Sys_Printf ("PCX file size: %d\n", fsize); lmp = LoadPCX (infile, false, NULL); if (!lmp) { Sys_Printf ("%s: Failed to load %s as texture.\n", this_program, inpath); goto die; } if (lmp->format != tex_palette) { Sys_Printf ("%s: %s is not a paletted image.\n", this_program, inpath); goto die; } else { // got a paletted image int32_t width = lmp->width; int32_t height = lmp->height; int lmp_size = width * height; if (options.verbosity > 1) Sys_Printf ("geometry: %dx%d\ndata size: %d\n", width, height, lmp_size); if (!options.raw) { // write lump header Qwrite (outfile, &width, sizeof (width)); Qwrite (outfile, &height, sizeof (height)); } if (Qwrite (outfile, lmp->data, lmp_size) != lmp_size) { Sys_Printf ("%s: Error writing to %s\n", this_program, outpath); goto die; } ret = true; } die: if (outpath) free (outpath); Qclose (infile); Qclose (outfile); return ret; } static int decode_args (int argc, char **argv) { int c; options.mode = mo_none; options.verbosity = 0; options.raw = false; options.width = options.height = -1; while ((c = getopt_long (argc, argv, "e" // export pcx "i" // import pcx "h" // show help "V" // show version "g:" // geometry "p:" // palette "r" // raw "q" // quiet "v" // verbose , long_options, (int *) 0)) != EOF) { switch (c) { case 'h': // help usage (0); break; case 'V': // version printf ("lmp version %s\n", PACKAGE_VERSION); exit (0); break; case 'e': // export options.mode = mo_export; break; case 'i': // import options.mode = mo_import; break; case 'g': { // set geometry char *tmp = strdup (optarg); char *blank = strrchr (tmp, 'x'); if (blank && *blank) { char *first, *second; first = tmp; second = blank + 1; options.width = atoi (first); options.height = atoi (second); } } break; case 'p': // palette options.palette = strdup (optarg); break; case 'r': // raw options.raw = 1; break; case 'q': // lower verbosity options.verbosity--; break; case 'v': // increase verbosity options.verbosity++; break; default: usage (1); } } return optind; } int main (int argc, char **argv) { QFile *infile; size_t ret; this_program = argv[0]; Sys_Init (); Memory_Init (malloc (MEMSIZE), MEMSIZE); decode_args (argc, argv); if (options.palette) { if ((infile = Qopen (options.palette, "rb"))) { // read in the palette palette = malloc (sizeof (quake_palette)); if (!(ret = Qread (infile, palette, sizeof (quake_palette)))) { Sys_Printf ("%s: failed to read palette from file \"%s\"\n", this_program, options.palette); Qclose (infile); exit (1); } } else { Sys_Printf ("%s: Couldn't open palette file \"%s\"\n", this_program, options.palette); } Qclose (infile); } else { palette = quake_palette; } switch (options.mode) { case mo_export: while (optind < argc) { if (options.verbosity > 0) Sys_Printf ("Exporting %s to PCX...\n", argv[optind]); exportFile (argv[optind]); optind++; } break; case mo_import: while (optind < argc) { if (options.verbosity > 0) Sys_Printf ("Converting %s to lump...\n", argv[optind]); importFile (argv[optind]); optind++; } break; default: Sys_Printf ("%s: No command given.\n", this_program); usage (1); } #if 0 for (i = 1; i < argc; i++) { int32_t width = 0; int32_t height = 0; QFile *outfile; char *in = argv[i]; char *out; unsigned char *line; int j; Sys_Printf ("%s\n", in); asprintf (&out, "%s.ppm", in); infile = Qopen (in, "rb"); outfile = Qopen (out, "wb"); // We use ASCII mode because we can put comments into it. Qprintf (outfile, "P3\n# %s\n", out); if (strcasestr (in, "conchars")) { // don't read w/h from conchars width = height = 128; } else { Qread (infile, &width, sizeof (width)); Qread (infile, &height, sizeof (height)); } Qprintf (outfile, "%d\n", width); Qprintf (outfile, "%d\n", height); Qprintf (outfile, "255\n"); for (j = 0; j < height; j++) { int k; line = malloc (width); // doing a line at a time ret = Qread (infile, line, width); for (k = 0; k < width; k++) { Sys_Printf ("%u %d %d\n", (unsigned) ret, k, line[k]); if (k > 239) Qprintf (outfile, "# next pixel fullbright\n"); Qprintf (outfile, "%3d %3d %3d\n", palette[line[k]].color.red, palette[line[k]].color.green, palette[line[k]].color.blue); } Qprintf (outfile, "# next line\n"); free (line); } Qclose (infile); Qclose (outfile); free (out); } #endif return 0; }