#include <stdio.h>
#include <fcntl.h>

#include "adltypes.h"
#include "adlprog.h"
#include "builtins.h"
#include "vstring.h"
#include "adlrun.h"


extern char
    *H_STR;


void special( void )
{
    int16
	t;

    assertargs( "$spec", 1 );	/* Have to have at least ($spec F) */
    switch( ARG( 1 ) ) {
    case 1 :
	debug = !debug;
	break;
    case 2 :
	restart();
	/* NOTREACHED */
    case 3 :
	head_term();
	exit( 0 );		/* terminate game */
    case 4 :
	assertargs( "$spec 4", 2 );
	savegame( virtstr( ARG( 2 ) ) );
	break;
    case 5 :
	assertargs( "$spec 5", 2 );
	if( restoregame( virtstr( ARG( 2 ) ) ) ) {
	    return;	/* Skip the fixup stuff */
	}
	break;
    case 6 :
	callextern();
	break;
    case 7 :
	do_wordfile();
	break;
    case 8 :
	scripter();
	break;
    case 9 :
	write_head();
	break;
    case 10 :
	assertargs( "$spec 10", 2 );
	scrwidth = ARG( 2 );
	break;
    case 11 :
	set_tty();
	break;
    case 12 :
	change_tty();
	break;
    case 13 :
	change_vec();
	break;
    default :
	error( ADLERR_SPEC );
    }
    popip();
    t = pop();
    sp = bp + 1;
    bp = t;
}

void restart( void )
{
    int
	i;

    restarted = 1;
    DO_EXIT( 4 );
}


void set_tty( void )
{
    error( ADLERR_SPEC_11 );
}


void change_tty( void )
{
    error( ADLERR_SPEC_12 );
}


void invert( char *s, int16 n )
{
    int16
	i;

    for( i = 0; i < n; i++ ) {
	s[ i ] ^= CODE_CHAR;
    }
}


void savegame( char *savename )
{
    FILE
	*savefile;
    int
	cmask;
    char
	tempstr[ SLEN ];
    int16
	temp,
	i;
    struct macro
	*m;

    RETVAL = 0;
    savefile = fopen( savename, "rb+" );
    if( savefile ) {
	fclose( savefile );
	sayer( "File " );
	sayer( savename );
	sayer( " already exists. Destroy it? " );
	if( !yesno() ) {
	    return;
	}
    }
    savefile = fopen( savename, "wb+" );
    if( !savefile ) {
	sayer( "Error opening file " );
	sayer( savename );
	sayer( "\n" );
	return;
    }
    invert( savec, numsave );
    for( i = 0; i < numact; i++ ) {
	strncpy( tempstr, actlist[ i ].linebuf, SLEN );
	strncpy( actlist[ i ].savebuf, tempstr, SLEN );
	actlist[ i ].linebuf = actlist[ i ].savebuf;
    }
    temp = nummacro();
    fwrite( &hdr.adlid,	1,	 sizeof( int32 ), savefile );
    fwrite( &numsave,	1,	 sizeof( int16 ), savefile );
    fwrite( savec,	1,	 numsave, savefile );
    fwrite( &numact,	1,	 sizeof( int16 ), savefile );
    fwrite( actlist,	numact,  sizeof( struct actrec ), savefile );
    fwrite( &vecsize,	1,	 sizeof( int16 ), savefile );
    fwrite( vecverb,	vecsize, sizeof( int16 ), savefile );
    fwrite( &currturn,	1,	 sizeof( int16 ), savefile );
    fwrite( &prompter,	1,	 sizeof( int16 ), savefile );
    fwrite( &numd,	1,	 sizeof( int16 ), savefile );
    fwrite( demons,	NUMDEM,  sizeof( int16 ), savefile );
    fwrite( &numf,	1,	 sizeof( int16 ), savefile );
    fwrite( fuses,	NUMFUS,  sizeof( int16 ), savefile );
    fwrite( ftimes,	NUMFUS,  sizeof( int16 ), savefile );
    fwrite( f_actors,	NUMFUS,  sizeof( int16 ), savefile );
    fwrite( objspace,	NUMOBJ,  sizeof( struct objrec ), savefile );
    fwrite( verbspace,	NUMVERB, sizeof( struct verbrec ), savefile );
    fwrite( stack,	NUMVAR,  sizeof( int16 ), savefile );
    fwrite( &temp,	1,	 sizeof( int16 ), savefile );
    for( m = mactab; m; m = m->next ) {
	invert( m->name, MLEN );
	invert( m->val, SLEN );
	fwrite( m->name, 1, MLEN, savefile );
	fwrite( m->val,  1, SLEN, savefile );
	invert( m->name, MLEN );
	invert( m->val, SLEN );
    }
    fclose( savefile );
#if UNIX
    cmask = umask( 0 );
    (void)umask( cmask );
    chmod( savename, 0666 & ~cmask );
#endif
    invert( savec, numsave );
    RETVAL = 1;
}


int restoregame( char *savename )
{
    FILE
	*savefile;
    char
	mname[ MLEN ],
	mval[ SLEN ];
    int32
	tempid;
    int16
	num,
	i;

    savefile = fopen( savename, "rb+" );
    if( !savefile ) {
	sayer( "Error opening file " );
	sayer( savename );
	sayer( "\n" );
	return 0;
    }
    fread( &tempid,	1, sizeof( int32 ), savefile );
    if( tempid != hdr.adlid ) {
	sayer( "Error: \"" );
	sayer( savename );
	sayer( "\" is not a save file for this game.\n" );
	return 0;
    }
    fread( &numsave,	1,	 sizeof( int16 ), savefile );
    fread( savec,	1,	 numsave, savefile );
    fread( &numact,	1,	 sizeof( int16 ), savefile );
    fread( actlist,	numact, sizeof( struct actrec ), savefile );
    fread( &vecsize,	1,	 sizeof( int16 ), savefile );
    free( vecverb );
    vecverb = (int16 *)calloc( vecsize, sizeof( int16 ) );
    if( vecverb == (int16 *)0 ) {
	error( ADLERR_MEMORY );
    }
    fread( vecverb,	vecsize, sizeof( int16 ), savefile );
    fread( &currturn,	1,	 sizeof( int16 ), savefile );
    fread( &prompter,	1,	 sizeof( int16 ), savefile );
    fread( &numd,	1,	 sizeof( int16 ), savefile );
    fread( demons,	NUMDEM, sizeof( int16 ), savefile );
    fread( &numf,	1,	 sizeof( int16 ), savefile );
    fread( fuses,	NUMFUS, sizeof( int16 ), savefile );
    fread( ftimes,	NUMFUS, sizeof( int16 ), savefile );
    fread( f_actors,	NUMFUS, sizeof( int16 ), savefile );
    fread( objspace,	NUMOBJ, sizeof( struct objrec ), savefile );
    fread( verbspace,	NUMVERB, sizeof( struct verbrec ), savefile );
    fread( stack,	NUMVAR, sizeof( int16 ), savefile );
    fread( &num,	1,	 sizeof( int16 ), savefile );
    clearmacro();
    for( i = 0; i < num; i++ ) {
	fread( mname,	1, MLEN, savefile );
	fread( mval,	1, SLEN, savefile );
	invert( mname, MLEN );
	invert( mval, SLEN );
	define( mname, mval );
    }
    fclose( savefile );

    /* Fix up the actlist string pointers, which may have changed since
       the file was saved. */
    for( i = 0; i < numact; i++ ) {
	actlist[ i ].linebuf = actlist[ i ].savebuf;
    }

    /* Decode the saved character buffer */
    invert( savec, numsave );

    /* It was a successful restore, so stop running */
    ip = 0L;
    bp = sp = NUMVAR;
    return 1;
}


void do_wordfile( void )
{
    assertargs( "$spec 7", 2 );
    if( wordwrite ) {
	fclose( wordfile );
    }
    wordwrite = 0;
    if( ARG( 2 ) ) {
	wordwrite = 1;
	wordfile = fopen( virtstr( ARG( 2 ) ), "a" );
    }
}


void callextern( void )
{
#if UNIX
    int
	i;
    char
	**argv,
	*t;

    /* Have to have at least ($spec 6 "name" 0) */
    assertargs( "$spec 6", 3 );
    if( fork() ) {
	/* We're the parent - wait for the child to die */
	RETVAL = wait( 0 );
    }
    else {
	/* We're the child. Get memory for the argument vector */
	argv = (char **)malloc( (RETVAL - 1) * sizeof( char * ) );
	if( argv == (char **)0 ) {
	    error( ADLERR_MEMORY );
	}

	/* Fill the argument vectors */
	for( i = 2; i < RETVAL && ARG( i ); i++ ) {
	    t = virtstr( ARG( i ) );
	    argv[ i - 2 ] = malloc( strlen( t ) + 1 );
	    if( argv[ i - 2 ] == (char *)0 ) {
		error( ADLERR_MEMORY );
	    }

	    strncpy( argv[ i - 2 ], t, SLEN );
	}

	/* Set the last argument to be zero */
	argv[ RETVAL - 2 ] = (char *)0;

	/* Execute the program */
	execv( argv[ 0 ], argv );

	/* In case the exec failed, exit. */
	head_term();
	exit( -1 );
    }
#endif
}


void change_vec( void )
{
    int16
	tempsize;

    assertargs( "$spec 13", 2 );
    tempsize = ARG( 2 );
    if( tempsize <= 0 ) {
	error( ADLERR_VERB_VEC );
    }
    if( tempsize != vecsize ) {
	vecsize = tempsize;
	free( vecverb );
	vecverb = (int16 *)calloc( vecsize, sizeof( int16 ) );
	if( vecverb == (int16 *)0 ) {
	    error( ADLERR_MEMORY );
	}
    }
}


void scripter( void )
{
    assertargs( "$spec 8", 2 );
    if( scriptfile != (FILE *)0 ) {
	fclose( scriptfile );
    }
    scriptfile = (FILE *)0;
    if( ARG( 2 ) ) {
	scriptfile = fopen( virtstr( ARG( 2 ) ), "w" );
    }
}

/*** EOF adlspec.c ***/
