/* 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. */ // // spritegen.c: generates a .spr file from a series of .lbm frame files. // Result is stored in /raid/quake/id1/sprites/<scriptname>.spr. // #include <errno.h> #include <math.h> #include <stdlib.h> #include <string.h> #include "QF/image.h" #include "QF/pcx.h" #include "QF/qendian.h" #include "QF/qtypes.h" #include "QF/quakeio.h" #include "QF/script.h" #include "QF/spritegn.h" #include "QF/sys.h" #define MAX_BUFFER_SIZE 0x100000 #define MAX_FRAMES 1000 tex_t *image; dsprite_t sprite; byte *lumpbuffer, *plump; char spritedir[1024]; char spriteoutname[1024]; int framesmaxs[2]; int framecount; script_t scr; typedef struct { spriteframetype_t type; // single frame or group of frames void *pdata; // either a dspriteframe_t or group info float interval; // only used for frames in groups int numgroupframes; // only used by group headers } spritepackage_t; spritepackage_t frames[MAX_FRAMES]; static void FinishSprite (void); static void Cmd_Spritename (void); /* ============ WriteFrame ============ */ static void WriteFrame (QFile *spriteouthandle, int framenum) { dspriteframe_t *pframe; dspriteframe_t frametemp; pframe = (dspriteframe_t *)frames[framenum].pdata; frametemp.origin[0] = LittleLong (pframe->origin[0]); frametemp.origin[1] = LittleLong (pframe->origin[1]); frametemp.width = LittleLong (pframe->width); frametemp.height = LittleLong (pframe->height); Qwrite (spriteouthandle, &frametemp, sizeof (frametemp)); Qwrite (spriteouthandle, (byte *)(pframe + 1), pframe->height * pframe->width); } /* ============ WriteSprite ============ */ static void WriteSprite (QFile *spriteouthandle) { int i, groupframe, curframe; dsprite_t spritetemp; sprite.boundingradius = sqrt (((framesmaxs[0] >> 1) * (framesmaxs[0] >> 1)) + ((framesmaxs[1] >> 1) * (framesmaxs[1] >> 1))); // // write out the sprite header // spritetemp.type = LittleLong (sprite.type); spritetemp.boundingradius = LittleFloat (sprite.boundingradius); spritetemp.width = LittleLong (framesmaxs[0]); spritetemp.height = LittleLong (framesmaxs[1]); spritetemp.numframes = LittleLong (sprite.numframes); spritetemp.beamlength = LittleFloat (sprite.beamlength); spritetemp.synctype = LittleFloat (sprite.synctype); spritetemp.version = LittleLong (SPR_VERSION); spritetemp.ident = LittleLong (IDHEADER_SPR); Qwrite (spriteouthandle, &spritetemp, sizeof(spritetemp)); // // write out the frames // curframe = 0; for (i=0 ; i<sprite.numframes ; i++) { Qwrite (spriteouthandle, &frames[curframe].type, sizeof(frames[curframe].type)); if (frames[curframe].type == SPR_SINGLE) { // // single (non-grouped) frame // WriteFrame (spriteouthandle, curframe); curframe++; } else { int j, numframes; dspritegroup_t dsgroup; float totinterval; groupframe = curframe; curframe++; numframes = frames[groupframe].numgroupframes; // // set and write the group header // dsgroup.numframes = LittleLong (numframes); Qwrite (spriteouthandle, &dsgroup, sizeof(dsgroup)); // // write the interval array // totinterval = 0.0; for (j=0 ; j<numframes ; j++) { dspriteinterval_t temp; totinterval += frames[groupframe+1+j].interval; temp.interval = LittleFloat (totinterval); Qwrite (spriteouthandle, &temp, sizeof(temp)); } for (j=0 ; j<numframes ; j++) { WriteFrame (spriteouthandle, curframe); curframe++; } } } } /* ============== LoadScreen ============== */ static void LoadScreen (const char *name) { QFile *file; printf ("grabbing from %s...\n",name); file = Qopen (name, "rb"); if (!file) Sys_Error ("could not open"); image = LoadPCX (file, false, 0); } /* =============== Cmd_Type =============== */ static void Cmd_Type (void) { Script_GetToken (&scr, false); if (!strcmp (Script_Token (&scr), "vp_parallel_upright")) sprite.type = SPR_VP_PARALLEL_UPRIGHT; else if (!strcmp (Script_Token (&scr), "facing_upright")) sprite.type = SPR_FACING_UPRIGHT; else if (!strcmp (Script_Token (&scr), "vp_parallel")) sprite.type = SPR_VP_PARALLEL; else if (!strcmp (Script_Token (&scr), "oriented")) sprite.type = SPR_ORIENTED; else if (!strcmp (Script_Token (&scr), "vp_parallel_oriented")) sprite.type = SPR_VP_PARALLEL_ORIENTED; else Sys_Error ("Bad sprite type\n"); } /* =============== Cmd_Beamlength =============== */ static void Cmd_Beamlength (void) { Script_GetToken (&scr, false); sprite.beamlength = atof (Script_Token (&scr)); } /* =============== Cmd_Load =============== */ static void Cmd_Load (void) { Script_GetToken (&scr, false); LoadScreen (Script_Token (&scr)); } /* =============== Cmd_Frame =============== */ static void Cmd_Frame (void) { int x,y,xl,yl,xh,yh,w,h; byte *screen_p; int linedelta; dspriteframe_t *pframe; int pix; Script_GetToken (&scr, false); xl = atoi (Script_Token (&scr)); Script_GetToken (&scr, false); yl = atoi (Script_Token (&scr)); Script_GetToken (&scr, false); w = atoi (Script_Token (&scr)); Script_GetToken (&scr, false); h = atoi (Script_Token (&scr)); if ((xl & 0x07) || (yl & 0x07) || (w & 0x07) || (h & 0x07)) Sys_Error ("Sprite dimensions not multiples of 8\n"); if ((w > 255) || (h > 255)) Sys_Error ("Sprite has a dimension longer than 255"); xh = xl+w; yh = yl+h; pframe = (dspriteframe_t *)plump; frames[framecount].pdata = pframe; frames[framecount].type = SPR_SINGLE; if (Script_TokenAvailable (&scr, false)) { Script_GetToken (&scr, false); frames[framecount].interval = atof (Script_Token (&scr)); if (frames[framecount].interval <= 0.0) Sys_Error ("Non-positive interval"); } else { frames[framecount].interval = 0.1; } if (Script_TokenAvailable (&scr, false)) { Script_GetToken (&scr, false); pframe->origin[0] = -atoi (Script_Token (&scr)); Script_GetToken (&scr, false); pframe->origin[1] = atoi (Script_Token (&scr)); } else { pframe->origin[0] = -(w >> 1); pframe->origin[1] = h >> 1; } pframe->width = w; pframe->height = h; if (w > framesmaxs[0]) framesmaxs[0] = w; if (h > framesmaxs[1]) framesmaxs[1] = h; plump = (byte *)(pframe + 1); screen_p = image->data + yl* image->width + xl; linedelta = image->width - w; for (y=yl ; y<yh ; y++) { for (x=xl ; x<xh ; x++) { pix = *screen_p; *screen_p++ = 0; // if (pix == 255) // pix = 0; *plump++ = pix; } screen_p += linedelta; } framecount++; if (framecount >= MAX_FRAMES) Sys_Error ("Too many frames; increase MAX_FRAMES\n"); } /* =============== Cmd_GroupStart =============== */ static void Cmd_GroupStart (void) { int groupframe; groupframe = framecount++; frames[groupframe].type = SPR_GROUP; frames[groupframe].numgroupframes = 0; while (1) { if (!Script_GetToken (&scr, true)) Sys_Error ("End of file during group"); if (!strcmp (Script_Token (&scr), "$frame")) { Cmd_Frame (); frames[groupframe].numgroupframes++; } else if (!strcmp (Script_Token (&scr), "$load")) { Cmd_Load (); } else if (!strcmp (Script_Token (&scr), "$groupend")) { break; } else { Sys_Error ("$frame, $load, or $groupend expected\n"); } } if (frames[groupframe].numgroupframes == 0) Sys_Error ("Empty group\n"); } /* =============== ParseScript =============== */ static void ParseScript (void) { while (1) { if (!Script_GetToken (&scr, true)) break; if (!strcmp (Script_Token (&scr), "$load")) { Cmd_Load (); } if (!strcmp (Script_Token (&scr), "$spritename")) { Cmd_Spritename (); } else if (!strcmp (Script_Token (&scr), "$type")) { Cmd_Type (); } else if (!strcmp (Script_Token (&scr), "$beamlength")) { Cmd_Beamlength (); } else if (!strcmp (Script_Token (&scr), "$sync")) { sprite.synctype = ST_SYNC; } else if (!strcmp (Script_Token (&scr), "$frame")) { Cmd_Frame (); sprite.numframes++; } else if (!strcmp (Script_Token (&scr), "$load")) { Cmd_Load (); } else if (!strcmp (Script_Token (&scr), "$groupstart")) { Cmd_GroupStart (); sprite.numframes++; } } } /* ============== Cmd_Spritename ============== */ static void Cmd_Spritename (void) { if (sprite.numframes) FinishSprite (); Script_GetToken (&scr, false); sprintf (spriteoutname, "%s%s.spr", spritedir, Script_Token (&scr)); memset (&sprite, 0, sizeof(sprite)); framecount = 0; framesmaxs[0] = -9999999; framesmaxs[1] = -9999999; lumpbuffer = malloc (MAX_BUFFER_SIZE * 2); // *2 for padding if (!lumpbuffer) Sys_Error ("Couldn't get buffer memory"); plump = lumpbuffer; sprite.synctype = ST_RAND; // default } /* ============== FinishSprite ============== */ static void FinishSprite (void) { QFile *spriteouthandle; if (sprite.numframes == 0) Sys_Error ("no frames\n"); if (!strlen(spriteoutname)) Sys_Error ("Didn't name sprite file"); if ((plump - lumpbuffer) > MAX_BUFFER_SIZE) Sys_Error ("Sprite package too big; increase MAX_BUFFER_SIZE"); spriteouthandle = Qopen (spriteoutname, "wb"); printf ("saving in %s\n", spriteoutname); WriteSprite (spriteouthandle); Qclose (spriteouthandle); printf ("spritegen: successful\n"); printf ("%d frame(s)\n", sprite.numframes); printf ("%d ungrouped frame(s), including group headers\n", framecount); spriteoutname[0] = 0; // clear for a new sprite } /* ============== main ============== */ int main (int argc, char **argv) { int i, bytes; QFile *file; char *buf; if (argc != 2) Sys_Error ("usage: spritegen file.qc"); i = 1; //SetQdirFromPath (argv[i]); //ExtractFilePath (argv[i], spritedir); // chop the filename // // load the script // file = Qopen (argv[i], "rt"); if (!file) Sys_Error ("couldn't open %s. %s", argv[i], strerror(errno)); bytes = Qfilesize (file); buf = malloc (bytes + 1); bytes = Qread (file, buf, bytes); buf[bytes] = 0; Qclose (file); Script_Start (&scr, argv[i], buf); ParseScript (); FinishSprite (); return 0; }