/*
 --> The MMIO Portable Input/Output functions
  -> Divine Entertainment GameDev Libraries

 Copyright © 1997 by Jake Stine and Divine Entertainment


 File: MMIO.C

 Description:
 Miscellaneous I/O routines.. used to solve some portability issues
 (like big/little endian machines and word alignment in structures )
 Also includes mikmod's ingenious error handling variable.

 Portability:
 All systems - all compilers


 -----------------------------------
 The way this module works - By Jake Stine [Air Richter]

 · _mm_iobase is for .WAD type files, where several files are all compressed
   into one large file.  Just use _mm_set_iobase to the start of the sub-file
   in the WAD and go along on your way.

 · _mm_read_I_UWORD and _mm_read_M_UWORD have distinct differences:
   the first is for reading data written by a little endian (intel) machine,
   and the second is for reading big endian (Mac, RISC, Alpha) machine data.

 · _mm_write functions work the same as the _mm_read functions.

 · _mm_write_string [which is unrelated to _mm_read_string] writes a string,
   attaching the appropriate CR/LF combo for the given OS (ie, don't attach
   any CR/LF yourself)

 · _mm_read_string is for reading binary strings.  It is basically the same
   as an fread of bytes.
*/                                                                                     

#include "mmio.h"
#include <string.h>

#define COPY_BUFSIZE  1024

static long _mm_iobase=0, temp_iobase=0;

CHAR   *_mm_error = NULL;
UBYTE  _mm_cpybuf[COPY_BUFSIZE];


void StringWrite(CHAR  *s, FILE *fp)
// Specialized file output procedure.  Writes a UWORD length and then a
// string of the specified length (no NULL terminator) afterward.
{
    int slen;

    if(s==NULL)
    {   _mm_write_I_UWORD(0,fp);
    } else
    {   _mm_write_I_UWORD(slen = strlen(s),fp);
        _mm_write_UBYTES(s,slen,fp);
    }
}

CHAR *StringRead(FILE *fp)
// Reads strings written out by StringWrite above:  a UWORD length followed
// by length characters.  A NULL is added to the string after loading.
{
    CHAR  *s;
    UWORD len;

    len = _mm_read_I_UWORD(fp);
    if(len==0) return NULL;

    if((s = (CHAR *)_mm_malloc(len+1)) == NULL) return NULL;
    _mm_read_UBYTES(s,len,fp);
    s[len] = 0;

    return s;
}


FILE *_mm_fopen(CHAR *fname, CHAR *attrib)
{
   FILE *fp;

   if((fp=fopen(fname,attrib)) == NULL)
      _mm_error = ERROR_OPENING_FILE;
   return fp;
}


int _mm_fseek(FILE *stream, long offset, int whence)
{
   return fseek(stream,(whence==SEEK_SET) ? offset+_mm_iobase : offset, whence);
}


long _mm_ftell(FILE *stream)
{
   return ftell(stream)-_mm_iobase;
}


long _mm_flength(FILE *stream)
{
   long tmp,tmp2;

   tmp = ftell(stream);
   fseek(stream,0,SEEK_END);
   tmp2 = ftell(stream);
   fseek(stream,tmp,SEEK_SET);
   return tmp2-tmp;
}


long _mm_iobase_get(void)
{
   return _mm_iobase;
}


void _mm_iobase_set(long iobase)
{
   temp_iobase = _mm_iobase;    // store old value in case of revert
   _mm_iobase  = iobase;   
}


// Sets the current file-position as the new _mm_iobase
void _mm_iobase_setcur(FILE *fp)
{
   temp_iobase = _mm_iobase;    // store old value in case of revert
   _mm_iobase  = ftell(fp);
}


// Reverts to the last known _mm_iobase value.
void _mm_iobase_revert(void)
{
   _mm_iobase  = temp_iobase;
}


// Procedure: _mm_copyfile
// Copies a given number of bytes from the source file to the destination
// file.  Returns 1 on error.
BOOL _mm_copyfile(FILE *fpi, FILE *fpo, ULONG len)
{
   ULONG todo;
   
   while(len)
   {  todo = (len > COPY_BUFSIZE) ? COPY_BUFSIZE : len;
      if(!fread(_mm_cpybuf, todo, 1, fpi))
      {  _mm_error = ERROR_END_OF_FILE;
         return 1;
      }
      if(!fwrite(_mm_cpybuf, todo, 1, fpo))
      {  _mm_error = ERROR_DISK_FULL;
         return 1;
      }
      len -= todo;
   }
   return 0;
}


void _mm_write_string(CHAR *data, FILE *fp)
{
    if(data!=NULL)
       _mm_write_UBYTES(data, strlen(data)+1, fp);

}


void _mm_fputs(FILE *fp, CHAR *data)
{
   if(data != NULL)
      _mm_write_UBYTES(data, strlen(data), fp);

#ifndef __UNIX__
   _mm_write_UBYTE(13,fp);
#endif
   _mm_write_UBYTE(10,fp);
}



/*************
// These have accompanying #define's in mmio.h

void _mm_write_SBYTE(SBYTE data, FILE *fp)
{
   fputc(data,fp);
}

void _mm_write_UBYTE(UBYTE data,FILE *fp)
{
   fputc(data,fp);
}
*/

#ifdef MM_BIG_ENDIAN

// --> Big Endian Write Functions

void _mm_write_M_UWORD(UWORD data,FILE *fp)
{
   _mm_write_UBYTE(data&0xff,fp);
   _mm_write_UBYTE(data>>8,fp);
}


void _mm_write_I_UWORD(UWORD data,FILE *fp)
{
   _mm_write_UBYTE(data>>8,fp);
   _mm_write_UBYTE(data&0xff,fp);
}


void _mm_write_M_ULONG(ULONG data,FILE *fp)
{
   _mm_write_M_UWORD(data&0xffff,fp);
   _mm_write_M_UWORD(data>>16,fp);
}


void _mm_write_I_ULONG(ULONG data,FILE *fp)
{
   _mm_write_I_UWORD(data>>16,fp);
   _mm_write_I_UWORD(data&0xffff,fp);
}

#else

// --> Little Endian Write Functions

void _mm_write_M_UWORD(UWORD data,FILE *fp)
{
   _mm_write_UBYTE(data>>8,fp);
   _mm_write_UBYTE(data&0xff,fp);
}


void _mm_write_I_UWORD(UWORD data,FILE *fp)
{
   _mm_write_UBYTE(data&0xff,fp);
   _mm_write_UBYTE(data>>8,fp);
}


void _mm_write_M_ULONG(ULONG data,FILE *fp)
{
   _mm_write_M_UWORD(data>>16,fp);
   _mm_write_M_UWORD(data&0xffff,fp);
}


void _mm_write_I_ULONG(ULONG data,FILE *fp)
{
   _mm_write_I_UWORD(data&0xffff,fp);
   _mm_write_I_UWORD(data>>16,fp);
}

#endif


void _mm_write_M_SWORD(SWORD data,FILE *fp)
{
   _mm_write_M_UWORD((UWORD)data,fp);
}


void _mm_write_I_SWORD(SWORD data,FILE *fp)
{
   _mm_write_I_UWORD((UWORD)data,fp);
}


void _mm_write_M_SLONG(SLONG data,FILE *fp)
{
   _mm_write_M_ULONG((ULONG)data,fp);
}


void _mm_write_I_SLONG(SLONG data,FILE *fp)
{
   _mm_write_I_ULONG((ULONG)data,fp);
}


#define DEFINE_MULTIPLE_WRITE_FUNCTION(type_name, type)       \
void                                                          \
_mm_write_##type_name##S (type *buffer, int number, FILE *fp) \
{                                                             \
   while(number>0)                                            \
   {  _mm_write_##type_name(*(buffer++),fp);                  \
      number--;                                               \
   }                                                          \
}

//DEFINE_MULTIPLE_WRITE_FUNCTION (SBYTE, SBYTE)
//DEFINE_MULTIPLE_WRITE_FUNCTION (UBYTE, UBYTE)

DEFINE_MULTIPLE_WRITE_FUNCTION (M_SWORD, SWORD)
DEFINE_MULTIPLE_WRITE_FUNCTION (M_UWORD, UWORD)
DEFINE_MULTIPLE_WRITE_FUNCTION (I_SWORD, SWORD)
DEFINE_MULTIPLE_WRITE_FUNCTION (I_UWORD, UWORD)

DEFINE_MULTIPLE_WRITE_FUNCTION (M_SLONG, SLONG)
DEFINE_MULTIPLE_WRITE_FUNCTION (M_ULONG, ULONG)
DEFINE_MULTIPLE_WRITE_FUNCTION (I_SLONG, SLONG)
DEFINE_MULTIPLE_WRITE_FUNCTION (I_ULONG, ULONG)


/**********
SBYTE _mm_read_SBYTE(FILE *fp)
{
   return(fgetc(fp));
}

UBYTE _mm_read_UBYTE(FILE *fp)
{
   return(fgetc(fp));
}
**********/


#ifdef MM_BIG_ENDIAN

UWORD _mm_read_I_UWORD(FILE *fp)
{
   UWORD result=((UWORD)_mm_read_UBYTE(fp))<<8;
   result|=_mm_read_UBYTE(fp);
   return result;
}

UWORD _mm_read_M_UWORD(FILE *fp)
{
   UWORD result=_mm_read_UBYTE(fp);
   result|=((UWORD)_mm_read_UBYTE(fp))<<8;
   return result;
}

ULONG _mm_read_I_ULONG(FILE *fp)
{
   ULONG result=((ULONG)_mm_read_M_UWORD(fp))<<16;
   result|=_mm_read_M_UWORD(fp);
   return result;
}

ULONG _mm_read_M_ULONG(FILE *fp)
{
   ULONG result=_mm_read_I_UWORD(fp);
   result|=((ULONG)_mm_read_I_UWORD(fp))<<16;
   return result;
}

#else

UWORD _mm_read_M_UWORD(FILE *fp)
{
   UWORD result=((UWORD)_mm_read_UBYTE(fp))<<8;
   result|=_mm_read_UBYTE(fp);
   return result;
}

UWORD _mm_read_I_UWORD(FILE *fp)
{
   UWORD result=_mm_read_UBYTE(fp);
   result|=((UWORD)_mm_read_UBYTE(fp))<<8;
   return result;
}

ULONG _mm_read_M_ULONG(FILE *fp)
{
   ULONG result=((ULONG)_mm_read_M_UWORD(fp))<<16;
   result|=_mm_read_M_UWORD(fp);
   return result;
}

ULONG _mm_read_I_ULONG(FILE *fp)
{
   ULONG result=_mm_read_I_UWORD(fp);
   result|=((ULONG)_mm_read_I_UWORD(fp))<<16;
   return result;
}

#endif

SWORD _mm_read_M_SWORD(FILE *fp)
{
   return((SWORD)_mm_read_M_UWORD(fp));
}

SWORD _mm_read_I_SWORD(FILE *fp)
{
   return((SWORD)_mm_read_I_UWORD(fp));
}

SLONG _mm_read_M_SLONG(FILE *fp)
{
   return((SLONG)_mm_read_M_ULONG(fp));
}

SLONG _mm_read_I_SLONG(FILE *fp)
{
   return((SLONG)_mm_read_I_ULONG(fp));
}


int _mm_read_string(CHAR *buffer, int number, FILE *fp)
{
    fread(buffer,1,number,fp);
    return !feof(fp);
}



#define DEFINE_MULTIPLE_READ_FUNCTION(type_name, type)       \
int                                                          \
_mm_read_##type_name##S (type *buffer, int number, FILE *fp) \
{                                                            \
   while(number>0)                                           \
   {  *(buffer++)=_mm_read_##type_name(fp);                  \
      number--;                                              \
   }                                                         \
   return !feof(fp);                                         \
}

//DEFINE_MULTIPLE_READ_FUNCTION (SBYTE, SBYTE)
//DEFINE_MULTIPLE_READ_FUNCTION (UBYTE, UBYTE)

DEFINE_MULTIPLE_READ_FUNCTION (M_SWORD, SWORD)
DEFINE_MULTIPLE_READ_FUNCTION (M_UWORD, UWORD)
DEFINE_MULTIPLE_READ_FUNCTION (I_SWORD, SWORD)
DEFINE_MULTIPLE_READ_FUNCTION (I_UWORD, UWORD)

DEFINE_MULTIPLE_READ_FUNCTION (M_SLONG, SLONG)
DEFINE_MULTIPLE_READ_FUNCTION (M_ULONG, ULONG)
DEFINE_MULTIPLE_READ_FUNCTION (I_SLONG, SLONG)
DEFINE_MULTIPLE_READ_FUNCTION (I_ULONG, ULONG)

