//
// Resource loaders for various data formats
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "ithelib.h"
#include "media.h"
#include <allegro.h>
#ifdef _WIN32
#include "jpeg/win/jpeglib.h" // What a crock
#include <io.h> // Because Windows doesn't have mkstemp
#include <fcntl.h> // ditto
#else
#include <jpeglib.h>
#endif
#include "core.hpp"
#include "gamedata.h"
#include "loadfile.h"
// Defines
#ifdef __BEOS__
#define P_tmpdir "/tmp"
#endif
// Strict checking for structure save (not for release code)
//#define STRUCT_CHECK
#ifdef STRUCT_CHECK
#define S_START() char msg[128];int checksum=0;
#define S_END(x) if(checksum != sizeof(x)) {sprintf(msg,"%s processed %d bytes instead of %d",#x,checksum,sizeof(x));ithe_panic("structure mismatch",msg);}
#define S_GETWI(x) x=igetl_i(f);checksum+=4;
#define S_GETWM(x) x=igetl_m(f);checksum+=4;
#define S_GETSHI(x) x=igetsh_i(f);checksum+=2;
#define S_GETSHM(x) x=igetsh_m(f);checksum+=2;
#define S_GETB(x) x=igetb(f);checksum++;
#define S_GETPTR(x) igetl_i(f);checksum+=4; // skip 4 bytes regardless
#define S_READ(x,y) iread(x,y,f);checksum+=y;
#define S_PUTWI(x) iputl_i(x,f);checksum+=4;
#define S_PUTWM(x) iputl_m(x,f);checksum+=4;
#define S_PUTSHI(x) iputsh_i(x,f);checksum+=2;
#define S_PUTSHM(x) iputsh_m(x,f);checksum+=2;
#define S_PUTB(x) iputb(x,f);checksum++;
#define S_PUTPTR(x) iputl_i(0,f);checksum+=4; // 4 compatability bytes
#define S_WRITE(x,y) iwrite(x,y,f);checksum+=y;
#define S_HACK(x) checksum+=x; // Adjust for compatability
#else
#define S_START()
#define S_END(x)
#define S_GETWI(x) x=igetl_i(f);
#define S_GETWM(x) x=igetl_m(f);
#define S_GETSHI(x) x=igetsh_i(f);
#define S_GETSHM(x) x=igetsh_m(f);
#define S_GETB(x) x=igetb(f);
#define S_GETPTR(x) igetl_i(f); // skip 4 bytes regardless of true length
#define S_READ(x,y) iread(x,y,f);
#define S_PUTWI(x) iputl_i(x,f);
#define S_PUTWM(x) iputl_m(x,f);
#define S_PUTSHI(x) iputsh_i(x,f);
#define S_PUTSHM(x) iputsh_m(x,f);
#define S_PUTB(x) iputb(x,f);
#define S_PUTPTR(x) iputl_i(0,f); // write 4 blank compatability bytes
#define S_WRITE(x,y) iwrite(x,y,f);
#define S_HACK(x)
#endif
// Variables
int MZ1_SavingGame=0;
int MZ1_LoadingGame=0;
// Functions
static BITMAP *iload_cel(char *filename);
static BITMAP *iload_pcx(char *filename);
static BITMAP *iload_jpeg(char *filename);
extern BITMAP *iload_png(char *filename);
static void UnPcx(unsigned char *in,unsigned char *out,int length);
static unsigned char *UnJPG(FILE *fp, int *w, int *h, int *bpp);
extern int getnum4char(char *name);
// Code
BITMAP *iload_bitmap(char *filename)
{
if(strstr(filename,".cel"))
return iload_cel(filename);
if(strstr(filename,".pcx"))
return iload_pcx(filename);
if(strstr(filename,".jpg"))
return iload_jpeg(filename);
if(strstr(filename,".png"))
return iload_png(filename);
if(strstr(filename,".CEL"))
return iload_cel(filename);
if(strstr(filename,".PCX"))
return iload_pcx(filename);
if(strstr(filename,".JPG"))
return iload_jpeg(filename);
if(strstr(filename,".PNG"))
return iload_png(filename);
return NULL;
}
/*
* load a CEL file using the IT-HE filesystem wrappers
*/
BITMAP *iload_cel(char *filename)
{
unsigned short w,h; // size of image
int len,ctr,x,y,col;
unsigned char *buf,*ptr; // conversion buffer
IFILE *fp;
BITMAP *img;
BITMAP *icb;
RGB p[256];
// Read the whole file
fp = iopen(filename);
// Good, we're still around
len=ifilelength(fp);
buf=(unsigned char *)M_get(1,len); // Allocate room
iread(buf,len,fp);
iclose(fp);
// OK, let's go
if(buf[0] != 0x19 || buf[1] != 0x91)
ithe_panic("CEL header invalid for image",filename);
w = buf[2]+(buf[3]<<8);
h = buf[4]+(buf[5]<<8);
len = w*h;
ptr = &buf[32];
// Build the conversion palette
for(ctr=0;ctr<256;ctr++)
{
p[ctr].r = *ptr++;
p[ctr].g = *ptr++;
p[ctr].b = *ptr++;
}
/*
// Set palette entry 0 to current transparency colour for >8bpp
p[0].r = ire_transparent_r;
p[0].g = ire_transparent_g;
p[0].b = ire_transparent_b;
*/
select_palette(p); // Use this for bpp conversion later on
// Allocate the sprite we will put the .CEL into
img = create_bitmap(w,h);
if(!img)
ithe_panic("Create bitmap failed for image",filename);
clear_to_color(img,ire_transparent);
// Allocate line conversion buffer
icb = create_bitmap_ex(8,w,2);
if(!icb)
ithe_panic("Create icb failed for image",filename);
// Convert the sprite line by line
ptr = &buf[800];
for(y=0;y<h;y++)
{
// Copy raw data to line buffer
memcpy(icb->line[0],ptr,w);
// Translate to native BPP in destination image
blit(icb,img,0,0,0,y,w,1);
// Enforce any transparency, and increment ptr accordingly
for(x=0;x<w;x++)
if(!*ptr++)
putpixel(img,x,y,ire_transparent);
}
// Free the temporary buffers
destroy_bitmap(icb);
M_free(buf);
unselect_palette();
// Convert to main palette if 8bpp
if(ire_bpp == 8)
{
ptr=img->line[0];
for(y=0;y<len;y++)
{
col=makecol(p[*ptr].r,p[*ptr].g,p[*ptr].b);
// No transparency
if(col == 0)
col=1;
if(ptr)
*ptr=col;
else
*ptr=ire_transparent;
ptr++;
}
}
return img;
}
/*
* load a PCX file using the IT-HE filesystem wrappers
*/
BITMAP *iload_pcx(char *filename)
{
BITMAP *spr; // Output
IFILE *fp; // VFS filepointer
unsigned char *buf; // Decoding buffer
unsigned char *raw; // Raw buffer
long fl; // file length
int w,h,len; // Various data items
int ptr=0; // For PCX-24 debanding
int ctr,ctr2,mode;
RGB pal[256];
unsigned int colour;
fp = iopen(filename);
// Good, we're still around
fl=ifilelength(fp)-128; // PCX file is HEADER:DATA:PAL
raw = M_get(1,fl);
iseek(fp,8,SEEK_SET); // Skip to the width
w = igetsh_i(fp)+1; // make 1-based from 0-based
h = igetsh_i(fp)+1;
iseek(fp,65,SEEK_SET); // Skip to the type byte
mode = igetc(fp);
if(mode != 1 && mode != 3)
{
sprintf((char *)raw,"%d planes in file %s",mode,filename);
ithe_panic("Funny number of bitplanes in PCX file:",(char *)raw);
}
// Get the length of the raw bitmap
spr = create_bitmap(w,h);
len = w*h;
// Allocate decompression buffer
buf = M_get(1,len*mode);
switch(mode)
{
// 8 bits per pixel
case 1:
iseek(fp,-768L,SEEK_END); // Seek to palette data (end-768)
for(ctr=0;ctr<256;ctr++)
{
pal[ctr].r = igetb(fp);
pal[ctr].g = igetb(fp);
pal[ctr].b = igetb(fp);
}
iseek(fp,128,SEEK_SET); // Seek back to start of RLE data
iread(raw,fl,fp);
UnPcx(raw,buf,len); // Undo the RLE coding
ptr=0;
for(ctr=0;ctr<h;ctr++)
{
for(ctr2=0;ctr2<w;ctr2++)
{
colour = makecol(pal[buf[ptr]].r,pal[buf[ptr]].g,pal[buf[ptr]].b);
if(buf[ptr])
putpixel(spr,ctr2,ctr,colour);
else
putpixel(spr,ctr2,ctr,ire_transparent);
ptr++;
}
}
break; // Finished
// 24 bits per pixel, true colour
case 3:
iseek(fp,128,SEEK_SET); // Seek back to start of RLE data
iread(raw,fl,fp);
UnPcx(raw,buf,len*3); // Undo the RLE coding
// Because PCX-24 is so strange, the RGB levels are banded.
// First we have a complete mono image of the red component
// Then a complete mono image of the Green component
// Finally a complete mono image of the Blue component
// The code below re-combines the three bands.
// The result is fed into makecol, to get the natively-packed word
// for the current video mode
ptr=0;
for(ctr=0;ctr<h;ctr++)
{
for(ctr2=0;ctr2<w;ctr2++)
{
colour = makecol(buf[ptr],buf[ptr+w],buf[ptr+(w*2)]);
putpixel(spr,ctr2,ctr,colour);
ptr++;
}
ptr+=(w*2);
}
break;
default:
sprintf((char *)buf,"%d planes in file %s",mode,filename);
ithe_panic("Funny number of bitplanes in PCX file:",(char *)buf);
}
M_free(buf);
M_free(raw);
iclose(fp); // Close the file
return spr;
}
/*
* UnPCX - Decode the RLE data into RAW bitmap info
*/
void UnPcx(unsigned char *in,unsigned char *out,int length)
{
unsigned char ob; // Output Byte
int ctr,ctr2;
for(ctr=0;ctr<length;ctr++) // Decode it all
{
ob=*in++;
if(ob > 0xbf) // If it's a run of colour
{
ctr2=(ob & 0x3f); // Get run length
ob=*in++; // Get run colour
memset(out,ob,ctr2); // Do the run
out+=ctr2; // Increment pointer
ctr+=ctr2-1; // Adjust RLE pointer
}
else
*out++=ob; // It was a single colour
}
return; // Finished
}
/* load_wav: Taken from Allegro, made to use my filesystem API
* Reads a RIFF WAV format sample file, returning a SAMPLE structure,
* or NULL on error.
*/
SAMPLE *iload_wav(char *filename)
{
IFILE *f;
char buffer[25];
int i;
int length, len;
int freq = 22050;
int bits = 8;
int channels = 1;
signed short s;
SAMPLE *spl = NULL;
*allegro_errno=0;
f = iopen(filename);
if (!f)
return NULL;
iread((unsigned char *)buffer, 12, f); /* check RIFF header */
if (memcmp(buffer, "RIFF", 4) || memcmp(buffer+8, "WAVE", 4))
goto getout;
while (!ieof(f)) {
if (iread((unsigned char *)buffer, 4, f) != 4)
break;
length = igetl_i(f); /* read chunk length */
if (memcmp(buffer, "fmt ", 4) == 0) {
i = igetsh_i(f); /* should be 1 for PCM data */
length -= 2;
if (i != 1)
goto getout;
channels = igetsh_i(f); /* mono or stereo data */
length -= 2;
if ((channels != 1) && (channels != 2))
goto getout;
freq = igetl_i(f); /* sample frequency */
length -= 4;
igetl_i(f); /* skip six bytes */
igetsh_i(f);
length -= 6;
bits = igetsh_i(f); /* 8 or 16 bit data? */
length -= 2;
if ((bits != 8) && (bits != 16))
goto getout;
}
else if (memcmp(buffer, "data", 4) == 0) {
len = length / channels;
if (bits == 16)
len /= 2;
spl = create_sample(bits, ((channels == 2) ? TRUE : FALSE), freq, len);
if (spl) {
if (bits == 8) {
iread(spl->data, length, f);
}
else {
for (i=0; i<len*channels; i++) {
s = igetsh_i(f);
((signed short *)spl->data)[i] = s^0x8000;
}
}
length = 0;
}
}
while (length > 0) { /* skip the remainder of the chunk */
if (igetc(f) == (unsigned char)EOF)
break;
length--;
}
}
getout:
iclose(f);
return spl;
}
/*
* load_JPG - As display_JPG but for sprites
*
*/
BITMAP *iload_jpeg(char *filename)
{
char msg[256];
IFILE *fp; // VFS file pointer
int w,h,bpp,x,y,ptr;
BITMAP *out;
unsigned char *buf;
fp = iopen(filename);
w=h=bpp=0;
buf = UnJPG(fp->fp, &w, &h, &bpp);
if(!buf)
ithe_panic("Jpeg: Could not allocate memory to decompress",filename);
iclose(fp);
out=create_bitmap(w,h);
if(!out)
ithe_panic("Jpeg: Out of memory loading .JPG file :",filename);
// It seems we have a result. Convert to native colour format
ptr=0;
switch(bpp)
{
case 8: // Greyscale
for(y=0;y<h;y++)
for(x=0;x<w;x++)
{
if(buf[ptr])
putpixel(out,x,y,makecol(buf[ptr],buf[ptr],buf[ptr]));
else
putpixel(out,x,y,ire_transparent);
ptr++;
}
break;
case 24:
case 32:
for(y=0;y<h;y++)
for(x=0;x<w;x++)
{
putpixel(out,x,y,makecol(buf[ptr+0],buf[ptr+1],buf[ptr+2]));
ptr+=3;
}
// putpixel(out,x,y,makecol(buf[ptr++],buf[ptr++],buf[ptr++]));
break;
default:
sprintf(msg,"Jpeg: Unsupported bit depth %d in JPEG file",bpp);
ithe_panic(msg,filename);
break;
}
M_free(buf); // ok
return out;
}
/*
* Jpeg reader:
*/
// Error handler
METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
char msgbuf[256];
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
// struct jpeg_error_mgr *myerr = cinfo->err;
/* Display the message */
(*cinfo->err->format_message)(cinfo,msgbuf);
ithe_panic("Jpeg decompression error:",msgbuf);
}
//
// Here's the decompressor
//
unsigned char *UnJPG(FILE *fp, int *w, int *h, int *bpp)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPARRAY buffer;
int row_stride;
unsigned char *outbuf,*op;
cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = my_error_exit;
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp); // FILE *fp
jpeg_read_header(&cinfo, TRUE);
jpeg_calc_output_dimensions(&cinfo);
*w = cinfo.output_width;
*h = cinfo.output_height;
outbuf = (unsigned char *)M_get(*w**h,cinfo.output_components);
if(!outbuf)
{
ilog_quiet("alloc (%d*%d*%d) failed in load_jpeg\n",*w,*h,cinfo.output_components);
return NULL;
}
op=outbuf;
jpeg_start_decompress(&cinfo);
row_stride = cinfo.output_width * cinfo.output_components;
buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,row_stride,1);
while (cinfo.output_scanline < cinfo.output_height)
{
jpeg_read_scanlines(&cinfo,buffer,1);
memcpy(op,buffer[0],row_stride);
op+=row_stride;
}
*bpp = cinfo.output_components*8; // 8 bit or 24 bit
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return outbuf;
}
/*
* iload_datafile - Load an allegro datafile from within a .RAR file
*/
DATAFILE *iload_datafile(char *filename)
{
char outname[1024];
DATAFILE *df;
if(!iload_tempfile(filename,outname))
ithe_panic("iload_datafile: could not make temp file for font",filename);
// Okay, now we have the thing, let Allegro at it
df=load_datafile(outname);
// And delete the temporary file.
remove(outname);
return df;
}
/*
* iload_tempfile - Create a temporary file from a .RAR entry
* Dest is set to the filename. You must remove the file afterwards.
*/
int iload_tempfile(char *filename, char *dest)
{
char outname[1024];
char fname[1024];
IFILE *fp; // VFS file pointer
int length,handle;
unsigned char *buf;
#ifdef _WIN32
char *unsafe; // For the unsafe system (Windows)
#endif
if(!loadfile(filename,fname))
return 0;
// ithe_panic("Could not load font: (file not found)",filename);
fp = iopen(fname);
length=fp->length; // Store file length for later
buf = M_get(1,length); // Read it into memory, so allocate some first
iread(buf,length,fp); // Read it in
iclose(fp);
// Create a temporary file
strcpy(outname,"IRXXXXXX");
#if defined(__SOMEUNIX__) || defined(__DJGPP__)
strcpy(outname,P_tmpdir);
strcat(outname,"/IRXXXXXX");
handle=mkstemp(outname);
if(handle == -1)
{
M_free(buf); // Free buffer up
return 0;
}
#else
// Humour Windows by doing it the unstable way
unsafe=tmpnam(NULL);
if(!unsafe)
{
M_free(buf); // Free buffer up
return 0;
}
handle=open(unsafe,_O_CREAT|_O_TRUNC|_O_BINARY|_O_RDWR,_S_IREAD|_S_IWRITE);
if(handle == -1)
{
M_free(buf); // Free buffer up
return 0;
}
strcpy(outname,unsafe);
#endif
length=write(handle,buf,length); // Write it out
close(handle);
M_free(buf); // Free buffer up
strcpy(dest,outname);
// Don't free the tmpnam buffer or windows will go apeshit
return 1;
}
/*
* load a CEL file using the IT-HE filesystem wrappers
* and without palette, so it can be used for lightmaps
*/
BITMAP *load_lightmap(char *filename)
{
unsigned short w,h; // size of image
int len;
unsigned char header[800]; // header
IFILE *fp;
BITMAP *img;
// Read the whole file
fp = iopen(filename);
// Good, we're still around. Read in the header
iread(header,800,fp);
// Check it's valid
if(header[0] != 0x19 || header[1] != 0x91)
ithe_panic("CEL header invalid for image",filename);
w = header[2]+(header[3]<<8);
h = header[4]+(header[5]<<8);
len = w*h;
// Allocate the sprite we will put the .CEL into
select_palette(default_palette); // Any old palette
img = create_bitmap_ex(8,w,h);
if(!img)
ithe_panic("Create bitmap failed for image",filename);
// Read the bitmap directly into memory
iread(img->line[0],len,fp);
// Close the file
iclose(fp);
// Restore old palette
unselect_palette();
return img;
}
void read_WORLD(WORLD *w,IFILE *f)
{
S_START();
S_GETWM(w->sig);
S_GETWI(w->w);
S_GETWI(w->h);
S_GETPTR(w->physmap);
S_GETPTR(w->object);
S_GETPTR(w->roof);
S_GETPTR(w->objmap);
S_GETPTR(w->light);
S_GETPTR(w->lightst);
S_GETWI(w->con_n);
S_GETWI(w->con_s);
S_GETWI(w->con_e);
S_GETWI(w->con_w);
// S_READ((unsigned char *)w->temp,25*sizeof(int *));
S_READ((unsigned char *)w->temp,25*4);
//S_END(WORLD);
}
void read_USEDATA(USEDATA *u,IFILE *f)
{
int ctr=0;
// 20 user slots
for(ctr=0;ctr<USEDATA_SLOTS;ctr++)
u->user[ctr]=igetl_i(f);
u->poison = igetl_i(f);
u->unconscious = igetl_i(f);
for(ctr=0;ctr<USEDATA_POTIONS;ctr++)
u->potion[ctr]=igetl_i(f);
u->dx = igetl_i(f);
u->dy = igetl_i(f);
u->vigilante = igetl_i(f);
u->edecor = igetl_i(f);
u->counter = igetl_i(f);
u->experience = igetl_i(f);
u->magic = igetl_i(f);
u->archive = igetl_i(f);
u->oldhp = igetl_i(f);
u->arcpocket.saveid = igetl_i(f); // Junk
u->pathgoal.saveid = igetl_i(f); // Junk
for(ctr=0;ctr<USEDATA_ACTSTACK;ctr++)
u->actlist[ctr]=igetl_i(f);
for(ctr=0;ctr<USEDATA_ACTSTACK;ctr++)
u->acttarget[ctr]=(void *)igetl_i(f); // Junk
u->actptr = igetl_i(f);
u->actlen = igetl_i(f);
u->fx_func = (int)igetl_i(f); // sign-extend to 64 bits as necessary
ctr = igetl_i(f); // Junk - NPCtalk
ctr = igetl_i(f); // Junk - lFlags
}
void write_USEDATA(USEDATA *u,IFILE *f)
{
int ctr=0;
long pstart,pend;
char emsg[128];
pstart = itell(f);
// 20 user slots
for(ctr=0;ctr<USEDATA_SLOTS;ctr++)
iputl_i(u->user[ctr],f);
iputl_i(u->poison,f);
iputl_i(u->unconscious,f);
for(ctr=0;ctr<USEDATA_POTIONS;ctr++)
iputl_i(u->potion[ctr],f);
iputl_i(u->dx,f);
iputl_i(u->dy,f);
iputl_i(u->vigilante,f);
iputl_i(u->edecor,f);
iputl_i(u->counter,f);
iputl_i(u->experience,f);
iputl_i(u->magic,f);
iputl_i(u->archive,f);
iputl_i(u->oldhp,f);
iputl_i(0,f); // ArcPocket ptr
iputl_i(0,f); // Pathgoal ptr
for(ctr=0;ctr<USEDATA_ACTSTACK;ctr++)
iputl_i(u->actlist[ctr],f);
for(ctr=0;ctr<USEDATA_ACTSTACK;ctr++)
iputl_i(0,f); // Junk for compatability
iputl_i(u->actptr,f);
iputl_i(u->actlen,f);
iputl_i((int)u->fx_func,f);
iputl_i(0,f); // Junk - NPCtalk
iputl_i(0,f); // Junk - lFlags
pend = itell(f);
if((pend - pstart) != USEDATA_VERSION)
{
sprintf(emsg,"%ld bytes instead of %d",pend-pstart,USEDATA_VERSION);
ithe_panic("USEDATA saved wrong number of bytes, change VERSION code",emsg);
}
}
#define IF_CHANGED(v) if(o->v != CHlist[type].v)
#define IF_S_CHANGED(v) if(stricmp(o->v,CHlist[type].v))
void TWriteObject(FILE *fp,OBJECT *o)
{
int type,ctr;
type = getnum4char(o->name);
// First line (declaration)
fprintf(fp,"\tobject %u\n",o->save_id);
// Object type
fprintf(fp,"\t\ttype %s\n",o->name);
// Location (coordinates or parent object)
if(o->parent.objptr && o->parent.objptr->save_id>0)
fprintf(fp,"\t\tinside %u\n",o->parent.objptr->save_id);
else
fprintf(fp,"\t\tat %ld %ld\n",o->x,o->y);
if(MZ1_SavingGame) // Do we care about these details?
{
// Animation Frame
fprintf(fp,"\t\tform %s\n",o->form->name);
// Sequence
fprintf(fp,"\t\tsequence %ld %ld %ld\n",o->sptr,o->form->frames,o->sdir);
}
// current direction
fprintf(fp,"\t\tcurdir %ld\n",o->curdir);
if(MZ1_SavingGame)
{
// Flags
if(o->flags != CHlist[type].flags)
fprintf(fp,"\t\tflags 0x%lx\n",o->flags);
}
if(MZ1_SavingGame)
{
// Width and Height
IF_CHANGED(w)
fprintf(fp,"\t\twidth %ld\n",o->w);
IF_CHANGED(h)
fprintf(fp,"\t\theight %ld\n",o->h);
// Width and Height (in tiles)
IF_CHANGED(mw)
fprintf(fp,"\t\tmwidth %ld\n",o->mw);
IF_CHANGED(mh)
fprintf(fp,"\t\tmheight %ld\n",o->mh);
}
// z (unused)
IF_CHANGED(z)
fprintf(fp,"\t\tz %ld\n",o->z);
// Personal Name
if(o->personalname)
if(stricmp(o->personalname,"-"))
if(stricmp(o->personalname,""))
fprintf(fp,"\t\tname %s\n",o->personalname);
// Target object
if(o->target.objptr)
fprintf(fp,"\t\ttarget %u\n",o->target.objptr->save_id);
// Target object
if(o->enemy.objptr)
fprintf(fp,"\t\tenemy %u\n",o->enemy.objptr->save_id);
// Tag
if(o->tag)
fprintf(fp,"\t\ttag %ld\n",o->tag);
// Current activity
if(o->activity>0)
fprintf(fp,"\t\tactivity %s\n",PElist[o->activity].name);
// Location tag (for long range pathfinder)
if(o->labels->location && strlen(o->labels->location)>0)
fprintf(fp,"\t\tlabels->location %s\n",o->labels->location);
IF_CHANGED(light)
fprintf(fp,"\t\tlight %ld\n",o->light);
//
// Stats
//
IF_CHANGED(stats->hp)
fprintf(fp,"\t\tstats->hp %ld\n",o->stats->hp);
IF_CHANGED(stats->dex)
fprintf(fp,"\t\tstats->dex %ld\n",o->stats->dex);
IF_CHANGED(stats->str)
fprintf(fp,"\t\tstats->str %ld\n",o->stats->str);
IF_CHANGED(stats->intel)
fprintf(fp,"\t\tstats->intel %ld\n",o->stats->intel);
IF_CHANGED(stats->weight)
fprintf(fp,"\t\tstats->weight %ld\n",o->stats->weight);
IF_CHANGED(maxstats->weight)
fprintf(fp,"\t\tmaxstats->weight %ld\n",o->maxstats->weight);
IF_CHANGED(stats->quantity)
fprintf(fp,"\t\tstats->quantity %ld\n",o->stats->quantity);
IF_CHANGED(stats->armour)
fprintf(fp,"\t\tstats->armour %ld\n",o->stats->armour);
IF_CHANGED(stats->damage)
fprintf(fp,"\t\tstats->damage %ld\n",o->stats->damage);
IF_CHANGED(stats->tick)
fprintf(fp,"\t\tstats->tick %ld\n",o->stats->tick);
if(o->stats->owner.objptr)
fprintf(fp,"\t\tstats->owner %u\n",o->stats->owner.objptr->save_id);
IF_CHANGED(stats->karma)
fprintf(fp,"\t\tstats->karma %ld\n",o->stats->karma);
IF_CHANGED(stats->bulk)
fprintf(fp,"\t\tstats->bulk %ld\n",o->stats->bulk);
IF_CHANGED(stats->range)
fprintf(fp,"\t\tstats->range %ld\n",o->stats->range);
IF_CHANGED(maxstats->speed)
fprintf(fp,"\t\tstats->speed %ld\n",o->maxstats->speed);
IF_CHANGED(stats->level)
fprintf(fp,"\t\tstats->level %ld\n",o->stats->level);
IF_CHANGED(stats->radius)
fprintf(fp,"\t\tstats->radius %ld\n",o->stats->radius);
IF_CHANGED(stats->alignment)
fprintf(fp,"\t\tstats->alignment %ld\n",o->stats->alignment);
if(MZ1_SavingGame)
fprintf(fp,"\t\tstats->npcflags 0x%lx\n",o->stats->npcflags);
//
// Funcs
//
IF_S_CHANGED(funcs->use)
fprintf(fp,"\t\tfuncs->use %s\n",o->funcs->use);
IF_S_CHANGED(funcs->talk)
fprintf(fp,"\t\tfuncs->talk %s\n",o->funcs->talk);
IF_S_CHANGED(funcs->kill)
fprintf(fp,"\t\tfuncs->kill %s\n",o->funcs->kill);
IF_S_CHANGED(funcs->look)
fprintf(fp,"\t\tfuncs->look %s\n",o->funcs->look);
IF_S_CHANGED(funcs->stand)
fprintf(fp,"\t\tfuncs->stand %s\n",o->funcs->stand);
IF_S_CHANGED(funcs->hurt)
fprintf(fp,"\t\tfuncs->hurt %s\n",o->funcs->hurt);
IF_S_CHANGED(funcs->init)
fprintf(fp,"\t\tfuncs->init %s\n",o->funcs->init);
IF_S_CHANGED(funcs->resurrect)
fprintf(fp,"\t\tfuncs->resurrect %s\n",o->funcs->resurrect);
IF_S_CHANGED(funcs->wield)
fprintf(fp,"\t\tfuncs->wield %s\n",o->funcs->wield);
IF_S_CHANGED(funcs->horror)
fprintf(fp,"\t\tfuncs->horror %s\n",o->funcs->horror);
IF_S_CHANGED(funcs->attack)
fprintf(fp,"\t\tfuncs->attack %s\n",o->funcs->attack);
IF_S_CHANGED(funcs->quantity)
fprintf(fp,"\t\tfuncs->quantity %s\n",o->funcs->quantity);
IF_S_CHANGED(funcs->user1)
fprintf(fp,"\t\tfuncs->user1 %s\n",o->funcs->user1);
IF_S_CHANGED(funcs->user2)
fprintf(fp,"\t\tfuncs->user2 %s\n",o->funcs->user2);
//
// Schedules
//
if(o->schedule)
{
for(ctr=0;ctr<24;ctr++)
if(o->schedule[ctr].active)
{
if(o->schedule[ctr].target.objptr)
fprintf(fp,"\t\tschedule %d %d %s %u\n",o->schedule[ctr].hour,o->schedule[ctr].minute,o->schedule[ctr].vrm,o->schedule[ctr].target.objptr->save_id);
else
fprintf(fp,"\t\tschedule %d %d %s %d\n",o->schedule[ctr].hour,o->schedule[ctr].minute,o->schedule[ctr].vrm,0);
}
}
fprintf(fp,"\n");
}