/****************************************************************************\
 *									      *
 *			C R E A T O R    O F   L E G E N D S		      *
 *				(AberMud Version 5)			      *
 *									      *
 *  The Creator Of Legends System is (C) Copyright 1989 Alan Cox, All Rights  *
 *  Reserved.		  						      *
 *									      *
 \****************************************************************************/

#include "System.h"

/*
 *	Disk Save/Load functions
 *
 *	1.00		AGC	First Version
 *	1.01		AGC	Saves substructures
 *	1.02		AGC	Table saving code
 *	1.03-1.06	AGC	Various tweaks, and substructure adds
 *	1.07		AGC	Saves flag names
 *	1.08		AGC	Runtime file format stuff
 *	1.09		AGC	Hashed text lists
 *	1.10		AGC	Register Optimisations
 *	1.11		AGC	Setin Setout Sethere
 *	1.12		AGC	UserTexts and frees texts right on Setin etc
 *	1.13		AGC	Bug Fixes For 5.06 Release
 *	1.14		AGC	Added Named table support
 *	1.15		AGC	Added 5.08 Item table support
 *	1.16		AGC	Fixed bug if you have single occurance of ""
 *	1.17		AGC	Fixed Unix errno problem.
 *	1.18		AGC	Fixed Unix errno problem properly
 *	1.19		AGC	Taught it about USERFLAG2 and CONDEXIT
 *	1.20		AGC	Tidied warnings out for 5.20
 *	1.21		AGC	Loads superclass and daemontable fields.
 *	1.22		AGC	Fixed SaveUniverse error that isnt bug
 *	1.23		AGC	Saves BSX pictures
 *	1.25		AGC	BSX save/load name bugfix
 */

Module	"DiskIO";
Version	"1.24";
Author  "----*(A)";

extern int errno;
extern ITEM *ItemList;
/*
 *	Items are saved by direct ordered dump, all text dumped is done
 *	literally at present, not through the text list.
 *
 *	Item links are converted to itemnumber in the master list
 *	save for ->masternext which is unsaved as it is implied
 *
 *	For loading an initial value gives the number of items to load
 *	the items are allocated (but not substructs) into a linked list
 *	and an array of their pointers kept for conversions. Each item
 *	is loaded, then the whole array is freed
 */


/* Global Declarations */

static void SaveShort();
static unsigned short LoadShort();
static void SaveLong();
static unsigned long LoadLong();
static void SaveItem();
static ITEM *LoadItem();
static void SaveString();
static TPTR LoadString();
static void SaveBSX();
static BSXImage *LoadBSX();
static void SaveWord();
static int LoadWord();
static void SaveVocab();
static void LoadVocab();
static void WriteHeader();
static long ReadHeader();
static void SaveSub();
static void LoadSub();
static void LoadObject();
static void SaveObject();
static void SetTwo();
static long GetTwo();
static unsigned short *SaveAction();
static short *LoadAction();
static void LoadLine();
static void SaveLine();
static void LoadTable();
static void SaveTable();
static void LoadAllTables();
static void SaveAllTables();
static TABLE *LoadItemTable();
static void SaveItemTable();

static ITEM **ItemArray;	/* Used for item loaders */
static long Load_Format=-1;
static int Load_Error=0;

static void SetTwo(x,v)
unsigned short *x;
register char *v;
{
	x[1]=((unsigned long)v)%65536;	/* Safe -we never extract this pointer */
	x[0]=((unsigned long)v)/65536;	/* directly so little endians should */
					/* be just fine.... CHANGE TO HTONS?? */
}

static long GetTwo(x)
register unsigned short *x;
{
	return(x[1]+65536L*x[0]);
}

static void SaveShort(file,v)
FILE *file;
register unsigned short v;
{
	unsigned char x[3];
/* Assumes 16 bit short - they are used as such even if bigger so is ok */
	x[0]=v/256;
	x[1]=v%256;
	if(fwrite(x,2,1,file)!=1)
		Load_Error=errno;
}

static unsigned short LoadShort(x)
FILE *x;
{
	unsigned char a[3];
	if(fread(a,2,1,x)!=1)
		Load_Error=errno;
	return((unsigned short)(256L*a[0]+a[1]));
}

static void SaveLong(file,x)
FILE *file;
register unsigned long x;
{
/*
 *	Assumes 32 bits - see notes above
 */
	SaveShort(file,(unsigned short)(x/65536));
	SaveShort(file,(unsigned short)(x%65536));
}

static unsigned long LoadLong(f)
FILE *f;
{
	unsigned short a=LoadShort(f);
	return((unsigned long)(a*65536L+LoadShort(f)));
}

static void SaveItem(file,i)
FILE *file;
ITEM *i;
{
	if(i==NULL)
		SaveLong(file,-1L);
	else
	{
		SaveLong(file,MasterNumber(i));
	}
}

static ITEM *LoadItem(file)
FILE *file;
{
	register long x=LoadLong(file);
	if(x==-1)
		return(NULL);
	return(ItemArray[x]);
}

static void SaveString(file,s)
FILE *file;
TPTR s;
{
	register char *str=TextOf(s);
	register long v=strlen(str);
	if(s->te_Users==1)
		v|=65536;
	SaveLong(file,v);
	if(fwrite(str,strlen(str),1,file)!=1)
		Load_Error=errno;
}

static TPTR LoadString(file)
FILE *file;
{
	register char *a;
	register long v=LoadLong(file);
	register TPTR t;
	register short f=0;
	if(v>=65536)
	{
		f=1;
		v-=65536;
	}
	a=malloc(v+1);
	if(!a)
	{
		Error("Out Of Memory");
	}
	if(fread(a,v,1,file)!=1)
	{
		Load_Error=errno;
	}
	a[v]=0;
	if(f)
		t=QuickAllocText(a);
	else
		t=AllocText(a);
	free(a);
	return(t);
}

static TPTR LoadComment(file)
FILE *file;
{
	register char *a;
	register long v=LoadLong(file);
	register TPTR t;
	if(v>65536)
	{
		v-=65536;
	}
	a=malloc(v+1);
	if(!a)
		Error("Out Of Memory");
	if(fread(a,v,1,file)!=1)
	{
		Load_Error=errno;
	}
	a[v]=0;
	t=AllocComment(a);
	free(a);
	return(t);
}

static void SaveBSX(file,i)
FILE *file;
BSXImage *i;
{
	int v=strlen(i->bsx_Identifier);
	SaveLong(file,v);
	SaveLong(file,i->bsx_DataSize);
	if(fwrite(i->bsx_Identifier,v,1,file)!=1)
		Load_Error=errno;
	if(fwrite(i->bsx_Data,i->bsx_DataSize,1,file)!=1)
		Load_Error=errno;
}

static BSXImage *LoadBSX(file)
FILE *file;
{
	extern BSXImage *BSXAllocate();
	BSXImage *i;
	char buf[128];
	long v=LoadLong(file);
	long d=LoadLong(file);
	if(fread(buf,v,1,file)!=1)
	{
		Load_Error=errno;
		return(NULL);
	}
	buf[v]=0;
	i=BSXAllocate(buf,d);
	if(fread(i->bsx_Data,d,1,file)!=1)
	{
		Load_Error=errno;
		BSXDelete(i);
		return(NULL);
	}
	return(i);
}

static void SaveWord(file,x)
FILE *file;
register WLIST *x;
{
/* NOTE: Words aren't allowed to contain spaces - THIS RELIES ON IT !!! */
	fprintf(file,"%s %d %d.",x->wd_Text,x->wd_Code,x->wd_Type);
}

static int LoadWord(file)
FILE *file;
{
	static char x[128];
	int a,b;
	if(fscanf(file,"%s %d %d.",x,&a,&b)!=3)
		Error("LoadWord: Corruption In Database File");
	if(strcmp(x,";END")==0)	/*; not legal in word either! */
		return(0);
	AddWord(x,(short)a,(short)b);
	return(1);
}

static void SaveVocab(file)
FILE *file;
{
	register WLIST *a=WordList;
	while(a)
	{
		SaveWord(file,a);
		a=a->wd_Next;
	}
	fprintf(file,";END 0 0.");
}

static void LoadVocab(file)
FILE *file;
{
	while(LoadWord(file));
}

static void WriteHeader(file)
FILE *file;
{
	long header[4];
	header[0]=CountItems();
	header[1]=10;		/* Format Identifier */
/*
 *	FORMATS:	0:	Original (no longer supported)
 *			1:	5.04 no flag names
 *			2:	5.04 flag names
 *			3:	5.05 vocab seek & classes
 *			4:	5.06 with text speed flags
 *			5:	5.07 with named tables
 *			6:	5.08 with item bound tables
 *		        7:      5.11 USERFLAG2 and CONDEXITs
 *			8:	5.14
 *			9:	5.20 superclass and daemon table
 *		       10:	5.21 BSX picture data
 */
	time(&(header[2]));	/* Save time */
	header[3]=0;		/* Reserved for vocab seek*/
	SaveLong(file,header[0]);
	SaveLong(file,header[1]);
	SaveLong(file,header[2]);
	SaveLong(file,header[3]);
}


static long ReadHeader(file)
FILE *file;
{
	long v[4];
	register long ct;
	v[0]=LoadLong(file);
	v[1]=LoadLong(file);
	v[2]=LoadLong(file);
	v[3]=LoadLong(file);
	Load_Format=v[1];
	if(Load_Format<1)
	{
		fprintf(stderr,
"This database format is too old.\n");
		exit(0);
	}
	if(Load_Format>10)
	{
		fprintf(stderr,
"This database format is beyond my knowledge, you need a newer SERVER.\n");
		exit(0);
	}
	Log("Header Read: %ld Items",v[0]);
	ct=0;
/*
 *	The +1 here stops a 0 sized malloc, which upsets some machines
 */
	ItemArray=(ITEM **)malloc(v[0]*sizeof(ITEM *)+1);
	if(ItemArray==NULL)
		Error("Out Of Memory");
	while(ct<v[0])
	{
		ItemArray[ct]=Allocate(ITEM);
		ItemArray[ct]->it_MasterNext=NULL;
		if(ct)
			ItemArray[ct-1]->it_MasterNext=ItemArray[ct];
		else
			ItemList=ItemArray[ct];
		ct++;
	}
	ItemArray[ct-1]->it_MasterNext=NULL;
/*
 *	The above creates a correctly ordered linked set of trash items
 */
	return(v[0]);
}

static void SaveSub(file,s)
FILE *file;
register SUB *s;
{
	SaveShort(file,s->pr_Key);
	switch(s->pr_Key)
	{
		case KEY_INOUTHERE:
			SaveString(file,((INOUTHERE *)s)->io_InMsg);
			SaveString(file,((INOUTHERE *)s)->io_OutMsg);
			SaveString(file,((INOUTHERE *)s)->io_HereMsg);
			break;
		case KEY_USERFLAG2:
		case KEY_USERFLAG:
			SaveShort(file,((USERFLAG *)s)->uf_Flags[0]);
			SaveShort(file,((USERFLAG *)s)->uf_Flags[1]);
			SaveShort(file,((USERFLAG *)s)->uf_Flags[2]);
			SaveShort(file,((USERFLAG *)s)->uf_Flags[3]);
			SaveShort(file,((USERFLAG *)s)->uf_Flags[4]);
			SaveShort(file,((USERFLAG *)s)->uf_Flags[5]);
			SaveShort(file,((USERFLAG *)s)->uf_Flags[6]);
			SaveShort(file,((USERFLAG *)s)->uf_Flags[7]);
			SaveItem(file,((USERFLAG *)s)->uf_Items[0]);
			SaveItem(file,((USERFLAG *)s)->uf_Items[1]);
			SaveItem(file,((USERFLAG *)s)->uf_Items[2]);
			SaveItem(file,((USERFLAG *)s)->uf_Items[3]);
			SaveItem(file,((USERFLAG *)s)->uf_Items[4]);
			SaveItem(file,((USERFLAG *)s)->uf_Items[5]);
			SaveItem(file,((USERFLAG *)s)->uf_Items[6]);
			SaveItem(file,((USERFLAG *)s)->uf_Items[7]);
			break;
		case KEY_CONTAINER:
			SaveShort(file,((CONTAINER *)s)->co_Volume);
			SaveShort(file,((CONTAINER *)s)->co_Flags);
			break;
		case KEY_CHAIN:
			SaveItem(file,((CHAIN *)s)->ch_Chained);
			break;
		case KEY_ROOM:
			SaveString(file,((ROOM *)s)->rm_Short);
			SaveString(file,((ROOM *)s)->rm_Long);
			SaveShort(file,((ROOM *)s)->rm_Flags);
			break;
		case KEY_OBJECT:
			SaveString(file,((OBJECT *)s)->ob_Text[0]);
			SaveString(file,((OBJECT *)s)->ob_Text[1]);
			SaveString(file,((OBJECT *)s)->ob_Text[2]);
			SaveString(file,((OBJECT *)s)->ob_Text[3]);
			SaveShort(file,((OBJECT *)s)->ob_Size);
			SaveShort(file,((OBJECT *)s)->ob_Weight);
			SaveShort(file,((OBJECT *)s)->ob_Flags);
			break;
		case KEY_PLAYER:
			SaveShort(file,((PLAYER *)s)->pl_UserKey);
			SaveShort(file,((PLAYER *)s)->pl_Size);
			SaveShort(file,((PLAYER *)s)->pl_Weight);
			SaveShort(file,((PLAYER *)s)->pl_Strength);
			SaveShort(file,((PLAYER *)s)->pl_Flags);
			SaveShort(file,((PLAYER *)s)->pl_Level);
			SaveLong(file,((PLAYER *)s)->pl_Score);
			break;
		case KEY_GENEXIT:
			SaveItem(file,((GENEXIT *)s)->ge_Dest[0]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[1]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[2]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[3]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[4]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[5]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[6]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[7]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[8]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[9]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[10]);
			SaveItem(file,((GENEXIT *)s)->ge_Dest[11]);
			break;
		case KEY_CONDEXIT:
			SaveItem(file,((CONDEXIT *)s)->ce_Dest);
			SaveShort(file,(short)((CONDEXIT *)s)->ce_Table);
			SaveShort(file,(short)((CONDEXIT *)s)->ce_ExitNumber);
			break;
		case KEY_MSGEXIT:
			SaveItem(file,((MSGEXIT *)s)->me_Dest);
			SaveString(file,((MSGEXIT *)s)->me_Text);
			SaveShort(file,((MSGEXIT *)s)->me_ExitNumber);
			break;
		case KEY_INHERIT:
			SaveItem(file,((INHERIT *)s)->in_Master);
			break;
		case KEY_DUPED:
			SaveItem(file,((DUP *)s)->du_Master);
			break;
		case KEY_USERTEXT:
			SaveString(file,((USERTEXT *)s)->ut_Text[0]);
			SaveString(file,((USERTEXT *)s)->ut_Text[1]);
			SaveString(file,((USERTEXT *)s)->ut_Text[2]);
			SaveString(file,((USERTEXT *)s)->ut_Text[3]);
			SaveString(file,((USERTEXT *)s)->ut_Text[4]);
			SaveString(file,((USERTEXT *)s)->ut_Text[5]);
			SaveString(file,((USERTEXT *)s)->ut_Text[6]);
			SaveString(file,((USERTEXT *)s)->ut_Text[7]);
			break;
		default:Error("Unknown Subtype");
	}
}

static void LoadSub(file,item,type)
FILE *file;
ITEM *item;
short type;
{
	TPTR xt;
	switch(type)
	{
		case KEY_INOUTHERE:
		{
			SetInMsg(item,TextOf(xt=LoadString(file)));
			FreeText(xt);
			SetOutMsg(item,TextOf(xt=LoadString(file)));
			FreeText(xt);
			SetHereMsg(item,TextOf(xt=LoadString(file)));
			FreeText(xt);
			break;
		}
		case KEY_USERTEXT:
		{
			SetUText(item,0,(xt=LoadString(file)));
			FreeText(xt);
			SetUText(item,1,(xt=LoadString(file)));
			FreeText(xt);
			SetUText(item,2,(xt=LoadString(file)));
			FreeText(xt);
			SetUText(item,3,(xt=LoadString(file)));
			FreeText(xt);
			SetUText(item,4,(xt=LoadString(file)));
			FreeText(xt);
			SetUText(item,5,(xt=LoadString(file)));
			FreeText(xt);
			SetUText(item,6,(xt=LoadString(file)));
			FreeText(xt);
			SetUText(item,7,(xt=LoadString(file)));
			FreeText(xt);
			break;
		}
		case KEY_CONTAINER:
		{
			register CONTAINER *c=BeContainer(item);
			c->co_Volume=LoadShort(file);
			c->co_Flags=LoadShort(file);
			break;
		}
		case KEY_USERFLAG:
		case KEY_USERFLAG2:
		{
			register USERFLAG *u;
			int x=0;
			if(type==KEY_USERFLAG2)
				x=8;
			SetUserFlag(item,0+x,LoadShort(file));
			SetUserFlag(item,1+x,LoadShort(file));
			SetUserFlag(item,2+x,LoadShort(file));
			SetUserFlag(item,3+x,LoadShort(file));
			SetUserFlag(item,4+x,LoadShort(file));
			SetUserFlag(item,5+x,LoadShort(file));
			SetUserFlag(item,6+x,LoadShort(file));
			SetUserFlag(item,7+x,LoadShort(file));
			if(type==KEY_USERFLAG)
				u=UserFlagOf(item);
			else
				u=UserFlag2Of(item);
			u->uf_Items[0]=LoadItem(file);
			u->uf_Items[1]=LoadItem(file);
			u->uf_Items[2]=LoadItem(file);
			u->uf_Items[3]=LoadItem(file);
			if(Load_Format>7)
			{
				u->uf_Items[4]=LoadItem(file);
				u->uf_Items[5]=LoadItem(file);
				u->uf_Items[6]=LoadItem(file);
				u->uf_Items[7]=LoadItem(file);
			}
			break;
		}
		case KEY_CHAIN:
		{
			ITEM *x=LoadItem(file);
			AddNLChain(item,x);
			break;
		}
		case KEY_ROOM:
		{
			register ROOM *a;
			MakeRoom(item);
			a=(ROOM *)FindSub(item,KEY_ROOM);
			FreeText(a->rm_Short);
			FreeText(a->rm_Long);
			a->rm_Short=LoadString(file);
			a->rm_Long=LoadString(file);
			a->rm_Flags=LoadShort(file);
			break;
		}
		case KEY_OBJECT:
		{
			register OBJECT *o;
			MakeObject(item);
			o=(OBJECT *)FindSub(item,KEY_OBJECT);
			FreeText(o->ob_Text[0]);
			FreeText(o->ob_Text[1]);
			FreeText(o->ob_Text[2]);
			FreeText(o->ob_Text[3]);	
			o->ob_Text[0]=LoadString(file);
			o->ob_Text[1]=LoadString(file);
			o->ob_Text[2]=LoadString(file);
			o->ob_Text[3]=LoadString(file);
			o->ob_Size=LoadShort(file);
			o->ob_Weight=LoadShort(file);
			o->ob_Flags=LoadShort(file);
			break;
		}
		case KEY_PLAYER:
		{
			register PLAYER *p;
			MakePlayer(item);
			p=(PLAYER *)FindSub(item,KEY_PLAYER);
			p->pl_UserKey=LoadShort(file);
			p->pl_Size=LoadShort(file);
			p->pl_Weight=LoadShort(file);
			p->pl_Strength=LoadShort(file);
			p->pl_Flags=LoadShort(file);
			p->pl_Level=LoadShort(file);
			p->pl_Score=LoadLong(file);
			break;
		}
		case KEY_GENEXIT:
		{
			register GENEXIT *g;
			MakeGenExit(item);
			g=(GENEXIT *)FindSub(item,KEY_GENEXIT);
			g->ge_Dest[0]=LoadItem(file);
			g->ge_Dest[1]=LoadItem(file);
			g->ge_Dest[2]=LoadItem(file);
			g->ge_Dest[3]=LoadItem(file);
			g->ge_Dest[4]=LoadItem(file);
			g->ge_Dest[5]=LoadItem(file);
			g->ge_Dest[6]=LoadItem(file);
			g->ge_Dest[7]=LoadItem(file);
			g->ge_Dest[8]=LoadItem(file);
			g->ge_Dest[9]=LoadItem(file);
			g->ge_Dest[10]=LoadItem(file);
			g->ge_Dest[11]=LoadItem(file);
			break;
		}
		case KEY_CONDEXIT:
		{
			register ITEM *a;
			short c,d;
			a=LoadItem(file);
			c=LoadShort(file);
			d=LoadShort(file);
			/* MakeCond... sets its own locks */
			MakeNLCondExit(item,a,c,d);
			break;
		}
		case KEY_MSGEXIT:
		{
			ITEM *a;
			TPTR b;
			short c;
			a=LoadItem(file);
			b=LoadString(file);
			c=LoadShort(file);
			MakeNLMsgExit(item,a,c,TextOf(b));
			FreeText(b);
			break;
		}
		case KEY_INHERIT:
		{
			ITEM *a=LoadItem(file);
			register INHERIT *d=(INHERIT *)AllocSub(item,KEY_INHERIT,
				sizeof(INHERIT));
			d->in_Master=a;
			break;
		}
		case KEY_DUPED:
		{
			ITEM *a=LoadItem(file);
			register DUP *d=(DUP *)AllocSub(item,KEY_DUPED,sizeof(DUP));
			d->du_Master=a;
			break;
		}
		default:Log("Subtype %d found!",type);
			Error("Unknown Subtype");
	}
}

static void LoadObject(file,i)
register FILE *file;
register ITEM *i;
{
	register long n;
	i->it_Name=LoadString(file);
/*	printf("%s\n",TextOf(i->it_Name));	*/
	i->it_Adjective=LoadShort(file);
	i->it_Noun=LoadShort(file);
	i->it_State=LoadShort(file);
	i->it_Perception=LoadShort(file);
	i->it_Next=LoadItem(file);
	i->it_Children=LoadItem(file);
	i->it_Parent=LoadItem(file);
	i->it_ActorTable=LoadShort(file);
	i->it_ActionTable=LoadShort(file);
	i->it_Users=LoadShort(file);
	i->it_Class=LoadShort(file);
	if(Load_Format>8)
	{
		if(LoadShort(file))
			i->it_Superclass=LoadItem(file);
		else
			i->it_Superclass=NULL;
	}
	i->it_Properties=NULL;
	n=LoadLong(file);	/* props */
	if(n==0)
		return;
	while(n)
	{
		n=LoadShort(file);
		if(n)
			LoadSub(file,i,(short)n);
	}
	if(Load_Format>5)
	{
		i->it_ObjectTable=LoadItemTable(file);
		i->it_SubjectTable=LoadItemTable(file);
		if(Load_Format>8)
			i->it_DaemonTable=LoadItemTable(file);
		else
			i->it_DaemonTable=NULL;
	}
	else
	{
		i->it_ObjectTable=NULL;
		i->it_SubjectTable=NULL;
	}
}
	
static void SaveObject(file,i)
register FILE *file;
register ITEM *i;
{
	SUB *n;
	KillEventQueue(i);
	SaveString(file,i->it_Name);
	SaveShort(file,i->it_Adjective);
	SaveShort(file,i->it_Noun);
	SaveShort(file,i->it_State);
	SaveShort(file,i->it_Perception);
	SaveItem(file,i->it_Next);
	SaveItem(file,i->it_Children);
	SaveItem(file,i->it_Parent);
	SaveShort(file,i->it_ActorTable);
	SaveShort(file,i->it_ActionTable);
	SaveShort(file,i->it_Users);
	SaveShort(file,i->it_Class);
	if(i->it_Superclass)
	{
		SaveShort(file,1);
		SaveItem(file,i->it_Superclass);
	}
	else
		SaveShort(file,0);
	SaveLong(file,(unsigned long)i->it_Properties);
	n=i->it_Properties;
	if(n==0)
		return;
	while(n)
	{
		SaveSub(file,n);
		n=n->pr_Next;
	}
	SaveShort(file,0);	/* Prop end marker */
	SaveItemTable(file,i->it_ObjectTable);
	SaveItemTable(file,i->it_SubjectTable);
	SaveItemTable(file,i->it_DaemonTable);	
}

unsigned short *SaveAction(file,c)
FILE *file;
register unsigned short *c;
{
	int cc=*c;
	TPTR t;
	ITEM *i;
	register char *ptr;
	SaveShort(file,*c++);
	ptr=Cnd_Table[cc];
	while(*ptr!=' ')
	{
		switch(*ptr++)
		{
		case '$':;
		case 'T':t=(TPTR )GetTwo(c);
			 c+=2;
			 if(t==(TPTR )1)
				SaveShort(file,0);
			 else
			 {
				if(t==(TPTR)3)
					SaveShort(file,3);
				else
				{
					SaveShort(file,1);
					SaveString(file,t);
				}
			 }
			 break;
		case 'I':i=(ITEM *)GetTwo(c);
			 c+=2;
			 if(i==(ITEM *)1)
			 {
				SaveShort(file,1);
				break;
			 }
			 if(i==(ITEM *)3)
			 {
				SaveShort(file,3);
				break;
			 }
			 if(i==(ITEM *)5)
			 {
				SaveShort(file,5);
				break;
			 }
			 if(i==(ITEM *)7)
			 {
				SaveShort(file,7);
				break;
			 }
			 if(i==(ITEM *)9)
			 {
				SaveShort(file,9);
				break;
			 }
			 SaveShort(file,0);
			 SaveItem(file,i);
			 break;
		case 'C':;
		case 'R':;
		case 'O':;
		case 'P':;
		case 'c':;
		case 'B':;
		case 'F':;
		case 'v':;
		case 'p':;
		case 'n':;
		case 'a':;
		case 't':;
		case 'N':SaveShort(file,*c++);
			 break;
		default:Error("Save: Bad Cnd_Table Entry");
		}
	}
	return(c);
}

short *LoadAction(file,c)
FILE *file;
register short *c;
{
	short cc=*c++;
	ITEM *i;
	TPTR t;
	register char *ptr=Cnd_Table[cc];
	while(*ptr!=' ')
	{
		switch(*ptr++)
		{
		case '$':switch(LoadShort(file))
			 {
				case 0:t=(TPTR)1;break;
				case 3:t=(TPTR)3;break;
				default:t=LoadComment(file);
			 }
			 SetTwo(c,(char *)t);
			 c+=2;
			 break;
		case 'T':switch(LoadShort(file))
			 {
				case 0:t=(TPTR )1;break;
				case 3:t=(TPTR) 3;break;
				default:t=LoadString(file);
			 }
			 SetTwo(c,(char *)t);
			 c+=2;
			 break;
		case 'I':switch(LoadShort(file))
			 {
				case 1:i=(ITEM *)1;break;
				case 3:i=(ITEM *)3;break;
				case 5:i=(ITEM *)5;break;
				case 7:i=(ITEM *)7;break;
				case 9:i=(ITEM *)9;break;
				default:i=LoadItem(file);
			 }
			 SetTwo(c,(char *)i);
			 c+=2;
			 break;
		case 'B':;
		case 'C':;
		case 'c':;
		case 'R':;
		case 'O':;
		case 'P':;
		case 'F':;
		case 'v':;
		case 'p':;
		case 'n':;
		case 'a':;
		case 't':;
		case 'N':*c++=LoadShort(file);
			 break;
		default:Error("Load: Bad Cnd_Table Entry");
		}
	}
	return(c);
}

void LoadLine(file,l)
FILE *file;
register LINE *l;
{
	short *a=(short *)malloc(1024);
	register short *b=a;
	if(a==NULL)
		Error("Out Of Memory");
	l->li_Verb=LoadShort(file);
	l->li_Noun1=LoadShort(file);
	l->li_Noun2=LoadShort(file);
	while((*b=LoadShort(file))!=CMD_EOL)
	{
		b=LoadAction(file,b);
	}
	free(l->li_Data);
	l->li_Data=(unsigned short *)malloc(sizeof(short)*(b-a+2));
	memcpy((char *)l->li_Data,(char *)a,sizeof(short)*(b-a+2));
	free((char *)a);
}

void SaveLine(file,l)
FILE *file;
LINE *l;
{
	unsigned short *a=l->li_Data;
	SaveShort(file,l->li_Verb);
	SaveShort(file,l->li_Noun1);
	SaveShort(file,l->li_Noun2);
	while(*a!=CMD_EOL)
	{
		a=SaveAction(file,a);
	}
	SaveShort(file,CMD_EOL);
}

TABLE *LoadItemTable(file)
FILE *file;
{
	TABLE *t;
	int x=LoadShort(file);
	if(x==0)
		return(NULL);
	t=Allocate(TABLE);
	LoadShort(file);
	LoadTable(file,t);
	return(t);
}

void SaveItemTable(file,t)
FILE *file;
TABLE *t;
{
	if(t==NULL)
	{
		SaveShort(file,0);
	}
	else
	{
		SaveShort(file,1);
		SaveTable(file,t);
	}
}

void LoadTable(file,t)
FILE *file;
TABLE *t;
{	
	register LINE *l;
	if(Load_Format>4)
		t->tb_Name=LoadString(file);
	else
		t->tb_Name=AllocText("(unset)");
	while(LoadShort(file)==0)
	{
		l=NewLine(t,65535);
		LoadLine(file,l);
	}
}

void SaveTable(file,t)
FILE *file;
TABLE *t;
{
	register LINE *l=t->tb_First;
	SaveShort(file,t->tb_Number);
	SaveString(file,t->tb_Name);
	while(l)
	{
		SaveShort(file,0);
		SaveLine(file,l);
		l=l->li_Next;
	}
	SaveShort(file,1);
}

void LoadAllTables(file)
FILE *file;
{
	register TABLE *t;
	while(LoadShort(file)==0)
	{
		t=NewTable(LoadShort(file),NULL);
		LoadTable(file,t);
	}
}

void SaveAllTables(file)
FILE *file;
{
	register TABLE *t=TableList;
	while(t)
	{
		SaveShort(file,0);
		SaveTable(file,t);
		t=t->tb_Next;
	}
	SaveShort(file,1);
}
	
extern char *FlagName[];

static void SaveFlag(file,s)
FILE *file;
short s;
{
	register char *str=FlagName[s];
	short v;
	if(str==NULL)
		SaveShort(file,0);
	else
	{
		v=strlen(str);
/*		printf("%d %ld\n",(int)s,v);	*/
		SaveShort(file,v);
		if(fwrite(str,strlen(str),1,file)!=1)
			Load_Error=errno;
	}
}


void LoadFlag(file,n)
FILE *file;
register short n;
{
	register char *a;
	long v=LoadShort(file);
/*	printf("FLAG %d V=%ld",(int)n,v);	*/
	if(v==0)
	{
		SetFlagName(n,NULL);
	}
	else
	{
		a=malloc(v+1);
		if(!a)
			Error("Out Of Memory");
		if(fread(a,v,1,file)!=1)
			Load_Error=errno;
		a[v]=0;
		SetFlagName(n,a);
		free(a);
	}
}

static void LoadClass(a,c)
FILE *a;
short c;
{
	if(LoadShort(a))
		SetClassTxt(c,LoadString(a));
	else
		SetClassName(c,NULL);
}

static void SaveClass(a,c)
FILE *a;
short c;
{
	if(GetClassTxt(c))
	{
		SaveShort(a,1);
		SaveString(a,GetClassTxt(c));
	}
	else
		SaveShort(a,0);
}
		
void SaveBitFlags(f)
FILE *f;
{
	int ct=0;
	while(ct<16)
	{
		fprintf(f,"%s\001",RBitName(ct));
		fprintf(f,"%s\001",OBitName(ct));
		fprintf(f,"%s\001",PBitName(ct));
		fprintf(f,"%s\001",CBitName(ct));
		ct++;
	}
}

static char *LoadDupStr(x)
char *x;
{
	char *a=strdup(x);
	if(!a)
	{
		fprintf(stderr,"Out Of Memory");
		exit(0);
	}
	return(a);
}

void LodBuf(f,x)
FILE *f;
char *x;
{
	while((*x=fgetc(f))!='\001')
		x++;
	*x=0;
}

void LoadBitFlags(f)
FILE *f;
{
	int ct=0;
	char buf[256];
	while(ct<16)
	{
		LodBuf(f,buf);
		RBitNames[ct]=LoadDupStr(buf);
		LodBuf(f,buf);
		OBitNames[ct]=LoadDupStr(buf);
		LodBuf(f,buf);
		PBitNames[ct]=LoadDupStr(buf);
		LodBuf(f,buf);
		CBitNames[ct]=LoadDupStr(buf);
		ct++;
	}
}

void SaveBSXImages(file)
FILE *file;
{
	extern BSXImage *BSXFindFirst(),*BSXFindNext();
	BSXImage *i=BSXFindFirst();
	while(i!=NULL)
	{
		SaveShort(file,1);
		SaveBSX(file,i);
		i=BSXFindNext(i);
	}
	SaveShort(file,0);
}

void LoadBSXImages(file)
FILE *file;
{
	int i;
	i=LoadShort(file);
	while(i!=0)
	{
		LoadBSX(file);
		i=LoadShort(file);
	}
}

int SaveSystem(n)
char *n;
{
	register ITEM *i;
	static char b[140];	/* Max file name length =128 */
	FILE *a=fopen(n,"r");
	register short c=0;
	sprintf(b,"%s.bak",n);
	if(a)
	{
		fclose(a);
		unlink(b);	/* Can't rename over another file */
		if(rename(n,b)==-1)
		{
			return(-1);
		}	/* TRY LINK(a,b) ON UNIX - OR
			   SEE /usr/src/bin/passwd.c FOR YOUR CLONE. ANYWAY 
			   RENAME IS ANSI ...*/
	}
/* Now to save */
	a=fopen(n,"w");
	if(a==NULL)
		return(-2);
	Load_Error=0;
	WriteHeader(a);
	i=ItemList;
	while(i)
	{
		SaveObject(a,i);
		i=i->it_MasterNext;
	}
	SaveAllTables(a);
	SaveVocab(a);
	while(c<512)
		SaveFlag(a,c++);
	c=0;
	while(c<16)
		SaveClass(a,c++);
	SaveBitFlags(a);
	SaveBSXImages(a);
	if(ferror(a)&&Load_Error==0)
		Load_Error=errno;
	fclose(a);
	return(Load_Error);
}

		
int LoadSystem(n)
char *n;
{
	FILE *a=fopen(n,"r");
	register long ct=0;
	long v,c=0;
	if(a==NULL)
		return(-1);
	Load_Error=0;
	v=ReadHeader(a);	/* Set up items */
	while(ct<v)
	{
		LoadObject(a,ItemArray[ct]);
		ct++;
	}
	LoadAllTables(a);
	free((char *)ItemArray);	/* Free Reloc Info */
	LoadVocab(a);
	if(Load_Format>1)	/* If new format database.. */
		while(c<512)
			LoadFlag(a,c++);
	if(Load_Format>2)	/* If 5.05 dbm load classes */
	{
		c=0;
		while(c<16)
			LoadClass(a,c++);
	}
	if(Load_Format>4)	/* 5.07 load bit flags too */
		LoadBitFlags(a);
	if(Load_Format>9)
		LoadBSXImages(a);
	fclose(a);
	Log("Load Read Completed");
	return(Load_Error);
}