#ifdef __STDC__
#include <stdlib.h>
#endif
#include <stdio.h>

/*
 * Copyright (c) 1999, J. Ali Harlow, J.A.Harlow@city.ac.uk.
 * Free for use with NetHack or any derivatives covered by its license.
 */

/*
 * This module deals with reading and writing basic NetHack types
 * from and to the NHXDR stream. Note that we only have to concern
 * ourselves with being able to read and write values that should
 * be present in a quantity, not what is physically possible on this
 * machine. For example, NetHack only stores values from -32768 to 32767
 * in an int. Any values found in an int outside this range represents
 * an illegal value and the result is udefined.
 */

int nhxdr_write_schar(sc)
schar sc;
{
    /*
     * The schar type has a range of possible values -128 to 127 (config.h).
     * We encode these as uchars. We map 0-127 onto 0-127 and -128 to -1
     * onto 128 to 255. On 2's compliment machines this results in the
     * same bit pattern, but we don't assume this.
     */
    return nhxdr_write_uchar(sc>=0?(uchar)sc:(uchar)(256+(int)sc));
}

int nhxdr_read_schar(sc)
schar *sc;
{
    /*
     * Reverse the process used when writing.
     */
    uchar uc;
    int retval;
    retval=nhxdr_read_uchar(&uc);
    if (!retval)
	*sc=uc<128?(schar)sc:(schar)((int)uc-256);
    return retval;
}

int nhxdr_write_xchar(xc)
xchar xc;
{
    /*
     * The xchar type has a range of possible values 0 to 127 (global.h).
     * It can be cast to uchar without loss.
     */
    return nhxdr_write_uchar((uchar)xc);
}

int nhxdr_read_xchar(xc)
xchar *xc;
{
    uchar uc;
    int retval;
    retval=nhxdr_read_uchar(&uc);
    if (!retval)
	*xc=uc;
    return retval;
}

int nhxdr_write_boolean(b)
boolean b;
{
    /*
     * The boolean type has a range of possible values 0 to 1 (global.h).
     * It can be cast to uchar without loss.
     */
    return nhxdr_write_uchar((uchar)b);
}

int nhxdr_read_boolean(b)
boolean *b;
{
    uchar uc;
    int retval;
    retval=nhxdr_read_uchar(&uc);
    if (!retval)
	*b=uc;
    return retval;
}

int nhxdr_write_int(i)
int i;
{
    /*
     * The int type has a range of possible values -32768 to 32767 (global.h).
     * 
     * We use the / operator because ANSI and K&R agree on its definition.
     *
     * Where i is an exact multiple of 256 then i/256 is the exact value.
     *
     * Where i is positive then i/256 is the largest integer less than
     * i/256.0, ie., quotient=floor(i/256.0).
     *
     * Where i is negative then i/256 is either
     *  a. The largest integer less than i/256.0 (ie., floor(i/256.0)), or
     *  b. The smallest integer greater than i/256.0 (ie., ceil(i/256.0)).
     *
     * In all cases (i/256)*256+i%256 == i
     *
     * Therefore:
     *
     * Where i is an exact multiple of 256 then i%256 is zero.
     *
     * Where i is positive then i%256 is positive.
     *
     * Where i is negative then i%256 is either
     *  a. positive, or
     *  b. negative.
     *
     * We normalize this by trapping the case of i%256 being negative and
     * adjusting to what we would have calculated had the machine been
     * of type a. The remainder must then have a value in the range 0 to 255.
     * The quotient must have a value in the range -128 to 127.
     */
    int quotient,remainder;
    quotient=i/256;
    remainder=i%256;
    if (remainder<0)
    {
	quotient--;
	remainder+=256;
    }
    return nhxdr_write_uchar((uchar)remainder) ||
      nhxdr_write_schar((schar)quotient);
}

int nhxdr_read_int(i)
int *i;
{
    uchar uc;
    schar sc;
    int retval;
    retval=nhxdr_read_uchar(&uc) || nhxdr_read_schar(&sc);
    if (!retval)
	*i=(int)(sc*256+uc);
    return retval;
}

int nhxdr_write_uint(ui)
unsigned int ui;
{
    /*
     * We assume that unsigned ints have a range of 0 to 65535.
     * 
     * These are just a much simpler case of ints (above), since
     * ui can never be negative. The range of both the quotient
     * and the remainder is 0 to 255.
     */
    int quotient,remainder;
    quotient=ui/256;
    remainder=ui%256;
    return nhxdr_write_uchar((uchar)remainder) ||
      nhxdr_write_uchar((uchar)quotient);
}

int nhxdr_read_uint(ui)
unsigned int *ui;
{
    uchar q,r;
    int retval;
    retval=nhxdr_read_uchar(&r) || nhxdr_read_uchar(&q);
    if (!retval)
	/*
	 * These casts are probably excessive, but we do have to
	 * be careful that nothing is promoted to type int, since
	 * that may not be able to store values larger than 32767.
	 */
	*ui=(unsigned int)q*(unsigned int)256+(unsigned int)r;
    return retval;
}

int nhxdr_write_short(si)
short int si;
{
    /*
     * short ints are no different to ints as far as we're concerned.
     */
    return nhxdr_write_int((int)si);
}

int nhxdr_read_short(si)
short int *si;
{
    int i;
    int retval;
    retval=nhxdr_read_int(&i);
    if (!retval)
	*si=i;
    return retval;
}

int nhxdr_write_ushort(usi)
unsigned short int usi;
{
    /*
     * And similarly unsigned short ints == unsigned ints.
     */
    return nhxdr_write_uint((unsigned int)usi);
}

int nhxdr_read_ushort(usi)
unsigned short int *usi;
{
    unsigned int ui;
    int retval;
    retval=nhxdr_read_uint(&ui);
    if (!retval)
	*usi=ui;
    return retval;
}

int nhxdr_write_long(l)
long l;
{
    /*
     * The long type is assumed to be at least 32-bit in lots of places
     * (eg., monst.h).
     * 
     * The problem is analogous to ints and is solved in the same way.
     */
    long quotient,remainder;
    quotient=i/65536;
    remainder=i%65536;
    if (remainder<0)
    {
	quotient--;
	remainder+=65536;
    }
    return nhxdr_write_uint((unsigned int)remainder) ||
      nhxdr_write_int((int)quotient);
}

int nhxdr_read_long(l)
long *l;
{
    unsigned int ui;
    int retval;
    retval=nhxdr_read_uint(&ui) || nhxdr_read_int(&i);
    if (!retval)
	*l=(long)i*256+ui;
    return retval;
}

int nhxdr_write_ulong(ul)
unsigned long ul;
{
    /*
     * unsigned longs are solved as unsigned ints.
     */
    long quotient,remainder;
    quotient=i/65536;
    remainder=i%65536;
    return nhxdr_write_uint((unsigned int)remainder) ||
      nhxdr_write_uint((unsigned int)quotient);
}

int nhxdr_read_ulong(ul)
unsigned long *ul;
{
    unsigned int q,r;
    int retval;
    retval=nhxdr_read_uint(&r) || nhxdr_read_uint(&q);
    if (!retval)
	*l=(unsigned long)q*256+r;
    return retval;
}

/*
 * NOTE: chars are _not_ small integers. Use uchar, schar, or xchar.
 *       chars contain characters from the execution character set.
 *
 * The following characters are assumed to exist in the execution
 * character set:
 *
 * The 52 upper-case and lower-case letters of the English alphabet
 * The 10 decimal digits
 * The following 29 graphic characters: !"#$&'()*+,-./:;<=>?[\]^_{|}~
 * The space character
 * The following 6 control characters: \0\n\t\b\r\f
 *
 * making 98 in all.
 *
 * ANSI defines a further 2 that are not present in K&R (\a and \v).
 *
 * The two remaining ASCII characters (@ and `) are missing, as
 * are DEL and the other 25 control codes. No values greater than
 * 127 are supported.
 *
 * We include @ and ` anyway since NetHack uses them. Any machine
 * that has problems with them will probably have problems with NetHack.
 *
 * Unsupported characters are written as 0xFF, which will be
 * converted to '?' on reading. A warning is issued if any unsupported
 * characters are read (which should never happen).
 *
 * There's no particular reason I standardised on ASCII, just writer's
 * privilege.
 */

static uchar nhxdr_charset[128]={
#ifdef __STDC__
  /* NUL - BEL */  '\0',  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  '\a',
  /* BS  - SI  */  '\b',  '\t',  '\n',  '\v',  '\f',  '\r',  0xFF,  0xFF,
#else
  /* NUL - BEL */  '\0',  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,
  /* BS  - SI  */  '\b',  '\t',  '\n',  0xFF,  '\f',  '\r',  0xFF,  0xFF,
#endif
  /* DLE - ETB */  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,
  /* CAN - US  */  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,  0xFF,
		    ' ',   '!',   '"',   '#',   '$',   '%',   '&',  '\'',
		    '(',   ')',   '*',   '+',   ',',   '-',   '.',   '/',
		    '0',   '1',   '2',   '3',   '4',   '5',   '6',   '7',
		    '8',   '9',   ':',   ';',   '<',   '=',   '>',   '?',
		    '@',   'A',   'B',   'C',   'D',   'E',   'F',   'G',
		    'H',   'I',   'J',   'K',   'L',   'M',   'N',   'O',
		    'P',   'Q',   'R',   'S',   'T',   'U',   'V',   'W',
		    'X',   'Y',   'Z',   '[',  '\\',   ']',   '^',   '_',
		    '`',   'a',   'b',   'c',   'd',   'e',   'f',   'g',
		    'h',   'i',   'j',   'k',   'l',   'm',   'n',   'o',
		    'p',   'q',   'r',   's',   't',   'u',   'v',   'w',
		    'x',   'y',   'z',   '{',   '|',   '}',   '~',  0xFF
};

static uchar nhxdr_rcharset[128]={0xFE};

int nhxdr_write_char(c)
char c;
{
    int i;
    if (nhxdr_rcharset[0]==0xFE)
    {
	for(i=0;i<128;i++)
	    nhxdr_rcharset[i]=0xFF;
	for(i=0;i<128;i++)
	    if (nhxdr_charset[i]!=0xFF)
		nhxdr_rcharset[nhxdr_charset[i]]=i;
    }
    i=c;	/* Avoid warnings from "clever" compilers */
    if (i>=0 && i<128)
	return nhxdr_write_uchar(nhxdr_rcharset[i]);
    else
	return nhxdr_write_uchar((uchar)0xFF);
}

int nhxdr_read_char(c)
char *c;
{
    uchar uc;
    int retval;
    retval=nhxdr_read_uchar(&uc);
    if (!retval)
    {
	if (uc==0xFF)
	    *c='?';
	else if (uc>128 || nhxdr_charset[uc]==0xFF)
	{
	    fprintf(stderr,
	      "Warning: Unsupported character in NHXDR stream (0x%02X)",uc);
	    *c='?';
	}
	else
	    *c=nhxdr_charset[uc];
    }
    return retval;
}
