/* be_le.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Little/Big-Endian-Routinen                                                */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"

#include <string.h>

#include "be_le.h"

/*****************************************************************************/

Boolean HostBigEndian;

const char *Integ16Format, *Integ32Format, *Integ64Format;
const char *IntegerFormat, *LongIntFormat, *QuadIntFormat;

/*****************************************************************************/

#define SWAP(x,y) \
do \
{ \
  Swap = x; x = y; y = Swap; \
} \
while (0)

void WSwap(void *Field, int Cnt)
{
  register unsigned char *Run = (unsigned char *) Field, Swap;
  register int z;

  for (z = 0; z < Cnt / 2; z++, Run += 2)
  {
    SWAP(Run[0], Run[1]);
  }
}

void DSwap(void *Field, int Cnt)
{
  register unsigned char *Run = (unsigned char *) Field, Swap;
  register int z;

  for (z = 0; z < Cnt / 4; z++, Run += 4)
  {
    SWAP(Run[0], Run[3]);
    SWAP(Run[1], Run[2]);
  }
}

void QSwap(void *Field, int Cnt)
{
  register unsigned char *Run = (unsigned char *) Field, Swap;
  register int z;

  for (z = 0; z < Cnt / 8; z++, Run += 8)
  {
    SWAP(Run[0], Run[7]);
    SWAP(Run[1], Run[6]);
    SWAP(Run[2], Run[5]);
    SWAP(Run[3], Run[4]);
  }
}

void TSwap(void *Field, int Cnt)
{
  register unsigned char *Run = (unsigned char *) Field, Swap;
  register int z;

  for (z = 0; z < Cnt / 10; z++, Run += 10)
  {
    SWAP(Run[0], Run[9]);
    SWAP(Run[1], Run[8]);
    SWAP(Run[2], Run[7]);
    SWAP(Run[3], Run[6]);
    SWAP(Run[4], Run[5]);
  }
}

void OSwap(void *Field, int Cnt)
{
  register unsigned char *Run = (unsigned char *) Field, Swap;
  register int z;

  for (z = 0; z < Cnt / 16; z++, Run += 16)
  {
    SWAP(Run[0], Run[15]);
    SWAP(Run[1], Run[14]);
    SWAP(Run[2], Run[13]);
    SWAP(Run[3], Run[12]);
    SWAP(Run[4], Run[11]);
    SWAP(Run[5], Run[10]);
    SWAP(Run[6], Run[ 9]);
    SWAP(Run[7], Run[ 8]);
  }
}

void DWSwap(void *Field, int Cnt)
{
  register unsigned char *Run = (unsigned char *) Field, Swap;
  register int z;

  for (z = 0; z < Cnt / 4; z++, Run += 4)
  {
    SWAP(Run[0], Run[2]);
    SWAP(Run[1], Run[3]);
  }
}

void QWSwap(void *Field, int Cnt)
{
  register unsigned char *Run = (unsigned char *) Field, Swap;
  register int z;

  for (z = 0; z < Cnt / 8; z++, Run += 8)
  {
    SWAP(Run[0], Run[6]);
    SWAP(Run[1], Run[7]);
    SWAP(Run[2], Run[4]);
    SWAP(Run[3], Run[5]);
  }
}

void TWSwap(void *Field, int Cnt)
{
  register unsigned char *Run = (unsigned char *) Field, Swap;
  register int z;

  for (z = 0; z < Cnt / 10; z++, Run += 10)
  {
    SWAP(Run[0], Run[8]);
    SWAP(Run[1], Run[9]);
    SWAP(Run[2], Run[6]);
    SWAP(Run[3], Run[7]);
    /* center word needs not be swapped with itself */
  }
}

Boolean Read2(FILE *file, void *Ptr)
{
  if (fread(Ptr, 1, 2, file) != 2)
    return False;
  if (HostBigEndian)
    WSwap(Ptr, 2);
  return True;
}

Boolean Read4(FILE *file, void *Ptr)
{
  if (fread(Ptr, 1, 4, file) != 4)
    return False;
  if (HostBigEndian)
    DSwap(Ptr, 4);
  return True;
}

Boolean Read8(FILE *file, void *Ptr)
{
  if (fread(Ptr, 1, 8, file) != 8)
    return False;
  if (HostBigEndian)
    QSwap(Ptr, 8);
  return True;
}


Boolean Write2(FILE *file, void *Ptr)
{
  Boolean OK;

  if (HostBigEndian)
    WSwap(Ptr, 2);
  OK = (fwrite(Ptr, 1, 2, file) == 2);
  if (HostBigEndian)
    WSwap(Ptr, 2);
  return OK;
}

Boolean Write4(FILE *file, void *Ptr)
{
  Boolean OK;

  if (HostBigEndian)
    DSwap(Ptr, 4);
  OK = (fwrite(Ptr, 1, 4, file) == 4);
  if (HostBigEndian)
    DSwap(Ptr, 4);
  return OK;
}

Boolean Write8(FILE *file, void *Ptr)
{
  Boolean OK;

  if (HostBigEndian)
    QSwap(Ptr, 8);
  OK = (fwrite(Ptr, 1, 8, file) == 8);
  if (HostBigEndian)
    QSwap(Ptr, 8);
  return OK;
}


Word MRead2L(Byte *Buffer)
{
  return (((Word) Buffer[1]) << 8) | Buffer[0];
}

Word MRead2B(Byte *Buffer)
{
  return (((Word) Buffer[0]) << 8) | Buffer[1];
}

void MWrite2L(Byte *Buffer, Word Value)
{
  Buffer[0] = Value & 0xff;
  Buffer[1] = (Value >> 8) & 0xff;
}

void MWrite2B(Byte *Buffer, Word Value)
{
  Buffer[1] = Value & 0xff;
  Buffer[0] = (Value >> 8) & 0xff;
}

LongWord MRead4L(Byte *Buffer)
{
  return (((LongWord) Buffer[3]) << 24) |
         (((LongWord) Buffer[2]) << 16) |
         (((LongWord) Buffer[1]) << 8)  | Buffer[0];
}

LongWord MRead4B(Byte *Buffer)
{
  return (((LongWord) Buffer[0]) << 24) |
         (((LongWord) Buffer[1]) << 16) |
         (((LongWord) Buffer[2]) << 8) | Buffer[3];
}

void MWrite4L(Byte *Buffer, LongWord Value)
{
  Buffer[0] = Value & 0xff;
  Buffer[1] = (Value >> 8) & 0xff;
  Buffer[2] = (Value >> 16) & 0xff;
  Buffer[3] = (Value >> 24) & 0xff;
}

void MWrite4B(Byte *Buffer, LongWord Value)
{
  Buffer[3] = Value & 0xff;
  Buffer[2] = (Value >> 8) & 0xff;
  Buffer[1] = (Value >> 16) & 0xff;
  Buffer[0] = (Value >> 24) & 0xff;
}

#ifdef AS_HAS64
QuadWord MRead8L(Byte *Buffer)
{
  return (((LargeWord) Buffer[7]) << 56) |
         (((LargeWord) Buffer[6]) << 48) |
         (((LargeWord) Buffer[5]) << 40) |
         (((LargeWord) Buffer[4]) << 32) |
         (((LargeWord) Buffer[3]) << 24) |
         (((LargeWord) Buffer[2]) << 16) |
         (((LargeWord) Buffer[1]) << 8)  |
                       Buffer[0];
}

QuadWord MRead8B(Byte *Buffer)
{
  return (((LargeWord) Buffer[0]) << 56) |
         (((LargeWord) Buffer[1]) << 48) |
         (((LargeWord) Buffer[2]) << 40) |
         (((LargeWord) Buffer[3]) << 32) |
         (((LargeWord) Buffer[4]) << 24) |
         (((LargeWord) Buffer[7]) << 16) |
         (((LargeWord) Buffer[6]) << 8)  |
                       Buffer[7];
}

void MWrite8L(Byte *Buffer, QuadWord Value)
{
  Buffer[0] = Value & 0xff;
  Buffer[1] = (Value >> 8) & 0xff;
  Buffer[2] = (Value >> 16) & 0xff;
  Buffer[3] = (Value >> 24) & 0xff;
  Buffer[4] = (Value >> 32) & 0xff;
  Buffer[5] = (Value >> 40) & 0xff;
  Buffer[6] = (Value >> 48) & 0xff;
  Buffer[7] = (Value >> 56) & 0xff;
}

void MWrite8B(Byte *Buffer, QuadWord Value)
{
  Buffer[7] = Value & 0xff;
  Buffer[6] = (Value >> 8) & 0xff;
  Buffer[5] = (Value >> 16) & 0xff;
  Buffer[4] = (Value >> 24) & 0xff;
  Buffer[3] = (Value >> 32) & 0xff;
  Buffer[2] = (Value >> 40) & 0xff;
  Buffer[1] = (Value >> 48) & 0xff;
  Buffer[0] = (Value >> 56) & 0xff;
}
#endif


static void CheckSingle(int Is, int Should, const char *Name)
{
  if (Is != Should)
  {
    fprintf(stderr, "Configuration error: Sizeof(%s) is %d, should be %d\n",
            Name, Is, Should);
    exit(255);
  }
}

static void CheckDataTypes(void)
{
  int intsize = sizeof(int);

  if (intsize < 2)
  {
    fprintf(stderr, "Configuration error: Sizeof(int) is %d, should be >=2\n",
            (int) sizeof(int));
    exit(255);
  }
  CheckSingle(sizeof(Byte),    1, "Byte");
  CheckSingle(sizeof(ShortInt), 1, "ShortInt");
#ifdef AS_HAS16
  CheckSingle(sizeof(Word),    2, "Word");
  CheckSingle(sizeof(Integer), 2, "Integer");
#endif
  CheckSingle(sizeof(LongInt), 4, "LongInt");
  CheckSingle(sizeof(LongWord), 4, "LongWord");
#ifdef AS_HAS64
  CheckSingle(sizeof(QuadInt), 8, "QuadInt");
  CheckSingle(sizeof(QuadWord), 8, "QuadWord");
#endif
#ifdef AS_HAS128
  CheckSingle(sizeof(OctaInt), 16, "OctaInt");
  CheckSingle(sizeof(OctaWord), 16, "OctaWord");
#endif
#ifdef AS_IEEEFLOAT_8_DOUBLE
  CheckSingle(sizeof(as_float_t),  8, "as_float_t");
#endif
#ifdef AS_IEEEFLOAT_10_10_LONG_DOUBLE
  CheckSingle(sizeof(as_float_t), 10, "as_float_t");
#endif
#ifdef AS_IEEEFLOAT_10_12_LONG_DOUBLE
  CheckSingle(sizeof(as_float_t), 12, "as_float_t");
#endif
#ifdef AS_IEEEFLOAT_10_16_LONG_DOUBLE
  CheckSingle(sizeof(as_float_t), 16, "as_float_t");
#endif
}

static const char *AssignSingle(int size)
{
  if (size == sizeof(short))
    return "%d";
  else if (size == sizeof(int))
    return "%d";
  else if (size == sizeof(long))
    return "%ld";
#if AS_HAS_LONGLONG
  else if (size == sizeof(long long))
    return "%lld";
#endif
  else
  {
    fprintf(stderr,
            "Configuration error: cannot assign format string for integer of size %d\n", size);
    exit(255);
    return "";
  }
}

static void AssignFormats(void)
{
#ifdef AS_HAS16
  IntegerFormat = Integ16Format = AssignSingle(2);
#endif
  LongIntFormat = Integ32Format = AssignSingle(4);
#ifdef AS_HAS64
  QuadIntFormat = Integ64Format = AssignSingle(8);
#endif
}

void be_le_init(void)
{
  union
  {
    unsigned char field[sizeof(int)];
    int test;
  } TwoFace;

  CheckDataTypes();
  AssignFormats();

  memset(TwoFace.field, 0, sizeof(int));
  TwoFace.field[0] = 1;
  HostBigEndian = ((TwoFace.test) != 1);
}
