/*************************************************************************
* unit:    struct            release 0.14                                *
*   struct.c - Provides some big and little endian abstraction functions,
*	      for manipulating with structures.
* Licency: GPL or LGPL                                                   *
*   Copyright (C) 1999-2023  Jaroslav Fojtik
**************************************************************************/
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>

#include "common.h"
#include "typedfs.h"

#include "struct.h"

#if defined(uint64_t) || defined(uint64_t_defined)
 #define QWORD_ uint64_t
#else
 #define QWORD_ double
#endif


#if __GNUC__>4 || (__GNUC__==4 && __GNUC_MINOR__>=3)
 #define BYTESWAP_uint64(__pval) __pval=__builtin_bswap64(__pval)
 #define BYTESWAP_uint32(__pval) __pval=__builtin_bswap32(__pval)
#else
 #if _MSC_VER > 1200	/* Not present in MSVC6 or less. */
  #include <stdlib.h>
  #define BYTESWAP_uint64(__pval) __pval=_byteswap_uint64(__pval)
  #define BYTESWAP_uint32(__pval) __pval=_byteswap_ulong(__pval)
 #endif
#endif

#ifndef BYTESWAP_uint64
 #define BYTESWAP_uint64(__pval) \
	uint8_t n; \
	uint8_t *x = (uint8_t *)&__pval; \
	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
#endif

#ifndef BYTESWAP_uint32
 #define BYTESWAP_uint32(__pval) __pval = \
	    ((__pval & 0xFF)<<24) | ((__pval & 0xFF00)<<8) | ((__pval & 0xFF0000)>>8) | ((__pval & 0xFF000000)>>24)
#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:     uint16_t *num : readed word  */
int RdWORD_LoEnd(uint16_t *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=(uint16_t)b;
  if(!fread( &b, 1,1,f)) return 1; *num+=(uint16_t)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:     uint32_t *num : readed double word  */
int RdDWORD_LoEnd(uint32_t *num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fread(num,1,4,f));
#endif
#ifdef HI_ENDIAN
const int i = fread(num,1,4,f);
  BYTESWAP_uint32(*num);
return i;
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
unsigned char b;
  if(!fread( &b, 1,1,f)) return 0; *num=(uint32_t)b;
  if(!fread( &b, 1,1,f)) return 1; *num+=(uint32_t)b * 256l;
  if(!fread( &b, 1,1,f)) return 2; *num+=(uint32_t)b * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 3; *num+=(uint32_t)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
const int i = fread(num,1,8,f);
  BYTESWAP_uint64(*num);
return i;
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
uint8_t 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:     uint16_t *num : readed word  */
int RdWORD_HiEnd(uint16_t *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=(uint16_t)b * 256l;
  if(!fread(&b,1,1,f)) return 1; *num+=(uint16_t)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:     uint32_t *num : readed double word  */
int RdDWORD_HiEnd(uint32_t *num, FILE *f)
{
#ifdef LO_ENDIAN
const int i = fread(num,1,4,f);
  BYTESWAP_uint32(*num);  
return i;
#endif
#ifdef HI_ENDIAN
  return(fread(num,1,4,f));
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
		//endian invariant
uint8_t b;
  if(!fread( &b, 1,1,f)) return 0; *num=(uint32_t)b * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 1; *num+=(uint32_t)b * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 2; *num+=(uint32_t)b * 256l;
  if(!fread( &b, 1,1,f)) return 3; *num+=(uint32_t)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:     uint32_t *num : readed double word  */
int RdQWORD_HiEnd(QWORD_ *num, FILE *f)
{
#ifdef LO_ENDIAN
const int i = fread(num,1,8,f);
  BYTESWAP_uint64(*num);
return i;
#endif
#ifdef HI_ENDIAN
  return(fread(num,1,8,f));
#endif
#if !defined(LO_ENDIAN) && !defined(HI_ENDIAN)
		//endian invariant
uint8_t b;
  if(!fread( &b, 1,1,f)) return 0; *num=(uint32_t)b  * 256l * 256l * 256l * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 1; *num+=(uint32_t)b * 256l * 256l * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 2; *num+=(uint32_t)b * 256l * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 3; *num+=(uint32_t)b * 256l * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 4; *num+=(uint32_t)b * 256l * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 5; *num+=(uint32_t)b * 256l * 256l;
  if(!fread( &b, 1,1,f)) return 6; *num+=(uint32_t)b * 256l;
  if(!fread( &b, 1,1,f)) return 7; *num+=(uint32_t)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
 *             uint16_t num : word to be written */
int WrWORD_LoEnd(uint16_t num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fwrite(&num,1,2,f));
#else
uint8_t b;
  b = (uint8_t)((num >> 8*0) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 0;
  b = (uint8_t)((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
 *             uint32_t num : double word to be written */
int WrDWORD_LoEnd(uint32_t num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fwrite(&num,1,4,f));
#else
#ifdef HI_ENDIAN
  BYTESWAP_uint32(num);
  return(fwrite(&num,1,4,f));
#else
uint8_t b;
  b = (uint8_t)((num >> 8*0) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 0;
  b = (uint8_t)((num >> 8*1) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 1;
  b = (uint8_t)((num >> 8*2) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 2;
  b = (uint8_t)((num >> 8*3) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 3;
return 4;
#endif
#endif
}
#endif


#ifndef WrWORD_HiEnd
/* Write a word to the binary file in a HighEndian Format
 * parameters: FILE  *f :  binary input file
 *             uint16_t num : word to be written */
int WrWORD_HiEnd(uint16_t num, FILE *f)
{
#ifdef HI_ENDIAN
  return(fwrite(&num,1,2,f));
#else
uint8_t b;
  b = (uint8_t)((num >> 8*1) & 0x00ffl); if(!fwrite( &b, 1,1,f)) return 0;
  b = (uint8_t)((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
 *             uint32_t num : double word to be written */
int WrDWORD_HiEnd(uint32_t num, FILE *f)
{
#ifdef HI_ENDIAN
  return(fwrite(&num,1,4,f));
#else
#ifdef LO_ENDIAN
  BYTESWAP_uint32(num);
  return(fwrite(&num,1,4,f));
#else
uint8_t b;
  b = (uint8_t)((num >> 8*3) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 0;
  b = (uint8_t)((num >> 8*2) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 1;
  b = (uint8_t)((num >> 8*1) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 2;
  b = (uint8_t)((num >> 8*0) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 3;
return 4;
#endif
#endif
}
#endif



#ifndef WrQWORD_HiEnd
/* Write a quad word to the binary file in a HiEndian Format
 * parameters: FILE  *f :  binary input file
 *             uint32_t num : double word to be written */
int WrQWORD_HiEnd(QWORD_ num, FILE *f)
{
#ifdef HI_ENDIAN
  return(fwrite(&num,1,8,f));
#else
#ifdef LO_ENDIAN
  BYTESWAP_uint64(num);
  return(fwrite(&num,1,8,f));
#else
uint8_t b;
  b = (uint8_t)((num >> 8*7) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 0;    
  b = (uint8_t)((num >> 8*6) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 1;
  b = (uint8_t)((num >> 8*5) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 2;
  b = (uint8_t)((num >> 8*4) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 3;  
  b = (uint8_t)((num >> 8*3) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 4;
  b = (uint8_t)((num >> 8*2) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 5;
  b = (uint8_t)((num >> 8*1) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 6;
  b = (uint8_t)((num >> 8*0) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 7;
return 8;
#endif
#endif
}
#endif


#ifndef WrQWORD_LoEnd
/* Write a quad word to the binary file in a LowEndian Format
 * parameters: FILE  *f :  binary input file
 *             uint32_t num : double word to be written */
int WrQWORD_LoEnd(QWORD_ num, FILE *f)
{
#ifdef LO_ENDIAN
  return(fwrite(&num,1,8,f));
#else
#ifdef HI_ENDIAN
  BYTESWAP_uint64(num);
  return(fwrite(&num,1,8,f));
#else
uint8_t b;
  b = (uint8_t)((num >> 8*0) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 0;
  b = (uint8_t)((num >> 8*1) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 1;
  b = (uint8_t)((num >> 8*2) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 2;
  b = (uint8_t)((num >> 8*3) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 3;  
  b = (uint8_t)((num >> 8*4) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 4;
  b = (uint8_t)((num >> 8*5) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 5;
  b = (uint8_t)((num >> 8*6) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 6;
  b = (uint8_t)((num >> 8*7) & 0x00FFl); if(!fwrite(&b, 1,1,f)) return 7;    
return 8;
#endif
#endif
}
#endif


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


/** Read a uint32_t from byte stream. */
uint32_t fil_sreadU32(const uint8_t *in)
{
#ifdef LO_ENDIAN
 return *(uint32_t *)in;
#else
 return (uint32_t)in[0] +
      ((uint32_t)in[1] << 8) +
      ((uint32_t)in[2] << 8*2) +
      ((uint32_t)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,uint16_t *),F);
     	      break;
     case 'W':loaded+=RdWORD_HiEnd(va_arg(ap,uint16_t *),F);
     	      break;
     case 'd':loaded+=RdDWORD_LoEnd(va_arg(ap,uint32_t *),F);
     	      break;
     case 'D':loaded+=RdDWORD_HiEnd(va_arg(ap,uint32_t *),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++;
   }
 va_end(ap);

return(loaded);
}


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

 va_start(ap, description);

 while(*description!=0)
   {
   //printf("%c",*description);fflush(stdout);
   switch(*description)
     {     
#ifndef __Ellipsis_Expand_Short
     case 'b':
     case 'B':{const char c = va_arg(ap,char);
	      writed+=fwrite(&c,1,1,F);}
              break;	
     case 'w':writed += WrWORD_LoEnd(va_arg(ap,uint16_t),F);
     	      break;
     case 'W':writed+=WrWORD_HiEnd(va_arg(ap,uint16_t),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,uint32_t),F);
     	      break;
     case 'D':writed += WrDWORD_HiEnd( va_arg(ap,uint32_t),F);
     	      break;

     case 'a':
     case 'A':ArraySize=0;
              while (isdigit(*(++description)))
                 {
                 ArraySize=10*ArraySize + (int)(*description-'0');
                 }
              description--;
              writed += fwrite(va_arg(ap,const char *), 1, ArraySize,F);
              break;
     case 'q':writed+=WrQWORD_LoEnd(va_arg(ap,QWORD_),F);
     	      break;
     case 'Q':writed+=WrQWORD_HiEnd(va_arg(ap,QWORD_),F);
     	      break;
     case 'e':{const QWORD_ e = va_arg(ap,QWORD_);
              writed+=WrQWORD_LoEnd(e,F);}
     	      break;
     case 'E':{const QWORD_ e = va_arg(ap,QWORD_);
              writed+=WrQWORD_HiEnd(e,F);}
     	      break;

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

   description++;
   }
 va_end(ap);

return(writed);
}
