mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 08:00:51 +00:00
437 lines
9.9 KiB
C
437 lines
9.9 KiB
C
/*
|
|
lmp.c
|
|
|
|
lump file tool
|
|
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
Copyright (C) 2002 Bill Currie <bill@taniwha.org>
|
|
Copyright (C) 2002,2011 Jeff Teunissen <deek@quakeforge.net>
|
|
|
|
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 <string.h>
|
|
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
|
|
#include <QF/cmd.h>
|
|
#include <QF/cvar.h>
|
|
#include <QF/image.h>
|
|
#include <QF/pcx.h>
|
|
#include <QF/qtypes.h>
|
|
#include <QF/qendian.h>
|
|
#include <QF/quakeio.h>
|
|
#include <QF/sys.h>
|
|
#include <QF/zone.h>
|
|
|
|
#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;
|
|
asprintf (&newstr, "%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 <command> [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];
|
|
|
|
Cvar_Init_Hash ();
|
|
Cmd_Init_Hash ();
|
|
Cvar_Init ();
|
|
Sys_Init_Cvars ();
|
|
Cmd_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;
|
|
}
|