/*
   STRUCT.C - Provides some big and little endian abstraction functions,
	      for manipulating with structures.	
   Copyright (C) 1999-2009  Jaroslav Fojtik
*/

#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>

#include "common.h"
#include "types.h"

#include "struct.h"

#ifdef QWORD
 #define QWORD_ QWORD
#else
 #define QWORD_ double
#endif


/* These procedures should be safe for all cases */

#ifndef RdWORD_LoEnd
/* Read a word from binary file in a LowEndian Format into a current endian.
 * parameters: FILE  *f :  binary input file
 * return:     WORD *num : readed word  */
int RdWORD_LoEnd(WORD *num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fread(num,1,2,f));
#endif
#ifdef HI_ENDIAN
int ret = fread(num,1,2,f);
  *num = ((*num & 0xFF)<<8) | ((*num & 0xFF00)>>8);
return ret;
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
unsigned char b;
  if(!fread( &b, 1,1,f)) return 0; *num=(WORD)b;
  if(!fread( &b, 1,1,f)) return 1; *num+=(WORD)b * 256l;
return 2;
#endif
}
#endif


#ifndef RdDWORD_LoEnd
/* Read a double word from binary file in a LowEndian Format into a current endian.
 * parameters: FILE  *f :  binary input file
 * return:     DWORD *num : readed double word  */
int RdDWORD_LoEnd(DWORD *num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fread(num,1,4,f));
#endif
#ifdef HI_ENDIAN
int i = fread(num,1,4,f);
  *num = ((*num & 0xFF)<<24) | ((*num & 0xFF00)<<8) |
	 ((*num & 0xFF0000)>>8) | ((*num & 0xFF000000)>>24);
return i;
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
unsigned char b;
  if(!fread( &b, 1,1,f)) return 0; *num=(DWORD)b;
  if(!fread( &b, 1,1,f)) return 1; *num+=(DWORD)b * 256l;
  if(!fread( &b, 1,1,f)) return 2; *num+=(DWORD)b * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 3; *num+=(DWORD)b * 256l * 256l * 256l;
return 4;
#endif
}
#endif


#ifndef RdQWORD_LoEnd
/* Read a double word from binary file in a LowEndian Format into a current endian.
 * parameters: FILE  *f :  binary input file
 * return:     QWORD *num : readed double word  */
int RdQWORD_LoEnd(QWORD_ *num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fread(num,1,8,f));
#endif
#ifdef HI_ENDIAN
int i = fread(num,1,8,f);
char n;
char *x = (char *)&num;
  n=x[0]; x[0]=x[7]; x[7]=n;
  n=x[1]; x[1]=x[6]; x[6]=n;
  n=x[2]; x[2]=x[5]; x[5]=n;
  n=x[3]; x[3]=x[4]; x[4]=n;
return i;
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
BYTE b;
  if(!fread( &b, 1,1,f)) return 0; *num=(QWORD_)b;
  if(!fread( &b, 1,1,f)) return 1; *num+=(QWORD_)b * 256l;
  if(!fread( &b, 1,1,f)) return 2; *num+=(QWORD_)b * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 3; *num+=(QWORD_)b * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 4; *num+=(QWORD_)b * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 5; *num+=(QWORD_)b * 256l * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 6; *num+=(QWORD_)b * 256l * 256l * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 7; *num+=(QWORD_)b * 256l * 256l * 256l * 256l * 256l * 256l * 256l;
return 8;  
#endif
}
#endif


#ifndef RdWORD_HiEnd
/* Read a word from binary file in a HiEndian Format
 * parameters: FILE  *f :  binary input file
 * return:     WORD *num : readed word  */
int RdWORD_HiEnd(WORD *num, FILE *f)
{
#ifdef LO_ENDIAN
int ret = fread(num,1,2,f);
  *num = ((*num & 0xFF)<<8) | ((*num & 0xFF00)>>8);
return ret;
#endif
#ifdef HI_ENDIAN
  return(fread(num,1,2,f));
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
		//endian invariant
unsigned char b;
  if(!fread(&b,1,1,f)) return 0; *num=(WORD)b * 256l;
  if(!fread(&b,1,1,f)) return 1; *num+=(WORD)b;
return 2;
#endif
}
#endif


#ifndef RdDWORD_HiEnd
/* Read a double word from binary file in a HighEndian Format
 * parameters: FILE  *f :  binary input file
 * return:     DWORD *num : readed double word  */
int RdDWORD_HiEnd( DWORD *num, FILE *f )
{
#ifdef LO_ENDIAN
int i = fread(num,1,4,f);
  *num = ((*num & 0xFF)<<24) | ((*num & 0xFF00)<<8) |
	 ((*num & 0xFF0000)>>8) | ((*num & 0xFF000000)>>24);
return i;
#endif
#ifdef HI_ENDIAN
  return(fread(num,1,4,f));
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
		//endian invariant
BYTE b;
  if(!fread( &b, 1,1,f)) return 0; *num=(DWORD)b * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 1; *num+=(DWORD)b * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 2; *num+=(DWORD)b * 256l;
  if(!fread( &b, 1,1,f)) return 3; *num+=(DWORD)b;
return 4;
#endif
}
#endif


#ifndef RdQWORD_HiEnd
/* Read a quad word from binary file in a HighEndian Format
 * parameters: FILE  *f :  binary input file
 * return:     DWORD *num : readed double word  */
int RdQWORD_HiEnd(QWORD_ *num, FILE *f )
{
#ifdef LO_ENDIAN
int i = fread(num,1,8,f);
BYTE n;
BYTE *x = (BYTE *)&num;
  n=x[0]; x[0]=x[7]; x[7]=n;
  n=x[1]; x[1]=x[6]; x[6]=n;
  n=x[2]; x[2]=x[5]; x[5]=n;
  n=x[3]; x[3]=x[4]; x[4]=n;
return i;
#endif
#ifdef HI_ENDIAN
  return(fread(num,1,8,f));
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
		//endian invariant
BYTE b;
  if(!fread( &b, 1,1,f)) return 0; *num=(DWORD)b  * 256l * 256l * 256l * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 1; *num+=(DWORD)b * 256l * 256l * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 2; *num+=(DWORD)b * 256l * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 3; *num+=(DWORD)b * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 4; *num+=(DWORD)b * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 5; *num+=(DWORD)b * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 6; *num+=(DWORD)b * 256l;
  if(!fread( &b, 1,1,f)) return 7; *num+=(DWORD)b;
  return 8;
#endif
}
#endif



#ifndef WrWORD_LoEnd
/* Write a word to the binary file in a LowEndian Format
 * parameters: FILE  *f :  binary input file
 *             WORD num : word to be written */
int WrWORD_LoEnd(WORD num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fwrite(&num,1,2,f));
#else
BYTE b;
  b = (BYTE)((num >> 8*0) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 0;
  b = (BYTE)((num >> 8*1) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 1;
return 2;
#endif
}
#endif


#ifndef WrDWORD_LoEnd
/* Write a double word to the binary file in a LowEndian Format
 * parameters: FILE  *f :  binary input file
 *             DWORD num : double word to be written */
int WrDWORD_LoEnd(DWORD num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fwrite(&num,1,4,f));
#else
BYTE b;
  b = (BYTE)((num >> 8*0) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 0;
  b = (BYTE)((num >> 8*1) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 1;
  b = (BYTE)((num >> 8*2) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 2;
  b = (BYTE)((num >> 8*3) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 3;
return 4;
#endif
}
#endif


#ifndef WrWORD_HiEnd
/* Write a word to the binary file in a HighEndian Format
 * parameters: FILE  *f :  binary input file
 *             WORD num : word to be written */
int WrWORD_HiEnd( WORD num, FILE *f )
{
#ifdef HI_ENDIAN
  return(fwrite(&num,1,2,f));
#else
BYTE b;
  b = (BYTE)((num >> 8*1) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 0;
  b = (BYTE)((num >> 8*0) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 1;
return 2;
#endif
}
#endif


#ifndef WrDWORD_HiEnd
/* Write a double word to the binary file in a HiEndian Format
 * parameters: FILE  *f :  binary input file
 *             DWORD num : double word to be written */
int WrDWORD_HiEnd( DWORD num, FILE *f )
{
#ifdef HI_ENDIAN
  return(fwrite(&num,1,4,f));
#else
BYTE b;
  b = (BYTE)((num >> 8*3) & 0x00FFl); if(!fwrite( &b, 1,1,f)) return 0;
  b = (BYTE)((num >> 8*2) & 0x00FFl); if(!fwrite( &b, 1,1,f)) return 1;
  b = (BYTE)((num >> 8*1) & 0x00FFl); if(!fwrite( &b, 1,1,f)) return 2;
  b = (BYTE)((num >> 8*0) & 0x00FFl); if(!fwrite( &b, 1,1,f)) return 3;
return 4;
#endif
}
#endif


/*Read a WORD from byte stream*/
WORD fil_sreadU16 (const BYTE *in)
{
#ifdef LO_ENDIAN
 return *(WORD *)in;
#else
 return (WORD)in[0] +
       ((WORD)in[1] << 8);
#endif
}


/*Read a DWORD from byte stream*/
DWORD fil_sreadU32 (const BYTE *in)
{
#ifdef LO_ENDIAN
 return *(DWORD *)in;
#else
 return (DWORD)in[0] +
      ((DWORD)in[1] << 8) +
      ((DWORD)in[2] << 8*2) +
      ((DWORD)in[3] << 8*3);
#endif
}




int loadstruct(FILE *F,const char *description, ...)
{
 va_list ap;
 int loaded=0;
 int ArraySize;

 va_start(ap, description);
 while(*description!=0)
   {

   switch(*description)
     {
     case 'b':
     case 'B':loaded+=fread(va_arg(ap,char *),1,1,F);
              break;
     case 'w':loaded+=RdWORD_LoEnd(va_arg(ap,WORD *),F);
     	      break;
     case 'W':loaded+=RdWORD_HiEnd(va_arg(ap,WORD *),F);
     	      break;
     case 'd':loaded+=RdDWORD_LoEnd(va_arg(ap,DWORD *),F);
     	      break;
     case 'D':loaded+=RdDWORD_HiEnd(va_arg(ap,DWORD *),F);
     	      break;
     case 'q':loaded+=RdQWORD_LoEnd(va_arg(ap,QWORD_ *),F);
     	      break;
     case 'Q':loaded+=RdQWORD_HiEnd(va_arg(ap,QWORD_ *),F);
     	      break;

     case 'a':
     case 'A':ArraySize=0;
     	      while (isdigit(*(++description)))
	         {
                 ArraySize = 10*ArraySize + (int)(*description-'0');
                 }
              description--;

	      loaded+=fread(va_arg(ap,char *),1,ArraySize,F);
              break;


     default:printf("Error: Unknown character %c !", *description);
     }

   description++;
   }

return(loaded);
}


int savestruct(FILE *F,const char *description, ...)
{
 va_list ap;
 int writed=0;
 int ArraySize;

 va_start(ap, description);
 while(*description!=0)
   {

   switch(*description)
     {     
#ifndef __Ellipsis_Expand_Short
     case 'b':
     case 'B':writed+=fwrite(&va_arg(ap,char),1,1,F);
              break;	
     case 'w':writed+=WrWORD_LoEnd( va_arg(ap,WORD),F);
     	      break;
     case 'W':writed+=WrWORD_HiEnd( va_arg(ap,WORD),F);
     	      break;
#else	//all shorts are internally converted to int
     case 'b':
     case 'B':{char c=va_arg(ap,int);
	      writed+=fwrite(&c,1,1,F);	      
              break;}
     case 'w':writed+=WrWORD_LoEnd( va_arg(ap,int),F);
     	      break;
     case 'W':writed+=WrWORD_HiEnd( va_arg(ap,int),F);
     	      break;
#endif
     case 'd':writed+=WrDWORD_LoEnd( va_arg(ap,DWORD),F);
     	      break;
     case 'D':writed+=WrDWORD_HiEnd( va_arg(ap,DWORD),F);
     	      break;

     case 'a':
     case 'A':ArraySize=0;
     	      while (isdigit(*(++description)))
	         {
                 ArraySize=10*ArraySize + (int)(*description-'0');
                 }
              description--;

              writed+=fwrite(va_arg(ap,char *),1,ArraySize,F);
              break;


     default:printf("Error: Unknown character %c !", *description);
     }

   description++;
   }

return(writed);
}

