/**************************************************************************
 *{@C
 *      Copyright:      2005-2018 Paul Obermeier (obermeier@tcl3d.org)
 *
 *                      See the file "Tcl3D_License.txt" for information on
 *                      usage and redistribution of this file, and for a
 *                      DISCLAIMER OF ALL WARRANTIES.
 *
 *      Module:         Tcl3D -> tcl3dOgl
 *      Filename:       tcl3dUtilPortable.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    Functions to convert integer and floating point
 *                      numbers between the processor's data format and
 *                      a machine-independent representation.
 *
 *      Exported functions:
 *                      tcl3dIsIntel
 *                      tcl3dGLbyteToByte
 *                      tcl3dGLubyteToByte
 *                      tcl3dGLshortToByte
 *                      tcl3dGLushortToByte
 *                      tcl3dGLintToByte
 *                      tcl3dGLuintToByte
 *                      tcl3dGLfloatToByte
 *                      tcl3dGLfloatTo3Byte
 *                      tcl3dGLfloatTo2Byte
 *                      tcl3dGLdoubleToByte
 *                      tcl3dByteToGLbyte
 *                      tcl3dByteToGLubyte
 *                      tcl3dByteToGLshort
 *                      tcl3dByteToGLushort
 *                      tcl3dByteToGLint
 *                      tcl3dByteToGLuint
 *                      tcl3dByteToGLfloat
 *                      tcl3d3ByteToGLfloat
 *                      tcl3d2ByteToGLfloat
 *                      tcl3dByteToGLdouble
 *
 **************************************************************************/

#include <string.h>
#include <math.h>

#include "tcl3dUtilPortable.h"

#define UT_ABS(x) ((x) >= 0? (x): -(x))

/***************************************************************************
 *[@e
 *      Name:           tcl3dIsIntel
 *
 *      Usage:          Determine at runtime, whether we are on an Intel system.
 *
 *      Synopsis:       int tcl3dIsIntel (void)
 *
 *      Description:    Determine at runtime, whether we are on an Intel system.
 *                      Intel systems have byte order little-endian.
 *
 *      Return value:   1, if processor architecture is Intel.
 *                      0, if processor architecure is Motorola.
 *
 *      See also:
 *
 ***************************************************************************/

int tcl3dIsIntel (void)
{
    unsigned long val = 513;
    /* On Intel (little-endian) systems this value is equal to "\01\02\00\00".
     *        On big-endian systems this value equals "\00\00\02\01" */
    return memcmp(&val, "\01\02", 2) == 0;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLbyteToByte
 *
 *      Usage:          Convert a GLbyte into a Byte vector.
 *
 *      Synopsis:       void tcl3dGLbyteToByte (GLbyte x, GLbyte buf[1])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void tcl3dGLbyteToByte (GLbyte x, GLbyte buf[1])
{
    buf[0] = x;
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLubyteToByte
 *
 *      Usage:          Convert a GLubyte into a Byte vector.
 *
 *      Synopsis:       void tcl3dGLubyteToByte (GLubyte x, GLbyte buf[1])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void tcl3dGLubyteToByte (GLubyte x, GLbyte buf[1])
{
    buf[0] = (GLubyte)(x);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLshortToByte
 *
 *      Usage:          Convert a GLshort into a Byte vector.
 *
 *      Synopsis:       void tcl3dGLshortToByte (GLshort x, GLbyte buf[2])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void tcl3dGLshortToByte (GLshort x, GLbyte buf[2])
{
    buf[1] = (GLbyte)(x);
    buf[0] = (GLbyte)(x >> 8);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLushortToByte
 *
 *      Usage:          Convert a GLushort into a Byte vector.
 *
 *      Synopsis:       void tcl3dGLushortToByte (GLushort x, GLbyte buf[2])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void tcl3dGLushortToByte (GLushort x, GLbyte buf[2])
{
    buf[1] = (GLubyte)(x);
    buf[0] = (GLubyte)(x >> 8);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLintToByte
 *
 *      Usage:          Convert an GLint into a Byte vector.
 *
 *      Synopsis:       void tcl3dGLintToByte (GLint x, GLbyte buf[4])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void tcl3dGLintToByte (GLint x, GLbyte buf[4])
{
    buf[3] = (GLbyte)(x);
    buf[2] = (GLbyte)(x >> 8);
    buf[1] = (GLbyte)(x >> 16);
    buf[0] = (GLbyte)(x >> 24);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLuintToByte
 *
 *      Usage:          Convert an GLuint into a Byte vector.
 *
 *      Synopsis:       void tcl3dGLuintToByte (GLuint x, GLbyte buf[4])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (Two's complement with the
 *                      most significant byte at the lowest address),
 *                      which can be used to transfer the value of "x"
 *                      between different types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void tcl3dGLuintToByte (GLuint x, GLbyte buf[4])
{
    buf[3] = (GLubyte)(x);
    buf[2] = (GLubyte)(x >> 8);
    buf[1] = (GLubyte)(x >> 16);
    buf[0] = (GLubyte)(x >> 24);
    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLfloatToByte
 *
 *      Usage:          Convert a GLfloat into a 4-Byte vector.
 *
 *      Synopsis:       void tcl3dGLfloatToByte (GLfloat x, GLbyte buf[4])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (IEEE single-precision
 *                      normalized number), which can be used to
 *                      transfer the value of "x" between different
 *                      types of processors.
 *
 *                      The tcl3dGLfloatToByte function converts "x" into a
 *                      four byte representation, which is sufficient to
 *                      preserve the value of "x" exactly for most processors.
 *
 *                      Conversion Function     Precision of Mantissa
 *                      -------------------------------------------------
 *                      tcl3dGLfloatToByte              23 bits
 *                      tcl3dGLfloatTo3Byte             15 bits
 *                      tcl3dGLfloatTo2Byte              7 bits
 *
 *      Return value:   None.
 *
 *      See also:       tcl3dGLfloatTo3Byte
 *                      tcl3dGLfloatTo2Byte
 *
 ***************************************************************************/

void tcl3dGLfloatToByte (GLfloat x, GLbyte buf[4])
{
    #if !defined (USE_EXACTCONV)

        if (tcl3dIsIntel ()) {
            buf[3] = ((GLbyte *)&x)[0];
            buf[2] = ((GLbyte *)&x)[1];
            buf[1] = ((GLbyte *)&x)[2];
            buf[0] = ((GLbyte *)&x)[3];
        } else {
            buf[0] = ((GLbyte *)&x)[0];
            buf[1] = ((GLbyte *)&x)[1];
            buf[2] = ((GLbyte *)&x)[2];
            buf[3] = ((GLbyte *)&x)[3];
        }
        return;

    #else

        double  m;
        int     exponent;
        GLint   mantissa;

        m = frexp ((double)x, &exponent);
        exponent += 126;

        if (m == 0.0 || exponent < 0) {
            buf[0] = 0;
            buf[1] = 0;
            buf[2] = 0;
            buf[3] = 0;
        } else {
            mantissa = (GLint)(UT_ABS(m) * (double)(1 << 24));
            buf[0] = (GLbyte)((m >= 0.0? 0x00 : 0x80) | ((exponent >> 1) & 0x7f));
            buf[1] = (GLbyte)((exponent << 7) | ((mantissa >> 16) & 0x7f));
            buf[2] = (GLbyte)(mantissa >> 8);
            buf[3] = (GLbyte)(mantissa);
        }
        return;
    #endif
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLfloatTo3Byte
 *
 *      Usage:          Convert a GLfloat into a 3-Byte vector.
 *
 *      Synopsis:       void tcl3dGLfloatTo3Byte (GLfloat x, GLbyte buf[3])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (IEEE single-precision
 *                      normalized number), which can be used to
 *                      transfer the value of "x" between different
 *                      types of processors.
 *
 *                      The tcl3dGLfloatTo3Byte function discards some of
 *                      the least significant bits of "x" in order to produce
 *                      a more compact representation of "x" than 
 *                      tcl3dGLfloatToByte.
 *
 *                      Conversion Function     Precision of Mantissa
 *                      -------------------------------------------------
 *                      tcl3dGLfloatToByte              23 bits
 *                      tcl3dGLfloatTo3Byte             15 bits
 *                      tcl3dGLfloatTo2Byte              7 bits
 *
 *      Return value:   None.
 *
 *      See also:       tcl3dGLfloatToByte
 *                      tcl3dGLfloatTo2Byte
 *
 ***************************************************************************/

void tcl3dGLfloatTo3Byte (GLfloat x, GLbyte buf[3])
{
    double      m;
    int         exponent;
    GLint       mantissa;

    m = frexp ((double)x, &exponent);
    exponent += 126;

    if (m == 0.0 || exponent < 0) {
        buf[0] = 0;
        buf[1] = 0;
        buf[2] = 0;
    } else {
        mantissa = (GLint)(UT_ABS(m) * (double)(1 << 24));
        buf[0] = (GLbyte)((m >= 0.0? 0x00 : 0x80) | ((exponent >> 1) & 0x7f));
        buf[1] = (GLbyte)((exponent << 7) | ((mantissa >> 16) & 0x7f));
        buf[2] = (GLbyte)(mantissa >> 8);
    }

    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLfloatTo2Byte
 *
 *      Usage:          Convert a GLfloat into a 2-Byte vector.
 *
 *      Synopsis:       void tcl3dGLfloatTo2Byte (GLfloat x, GLbyte buf[2])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (IEEE single-precision
 *                      normalized number), which can be used to
 *                      transfer the value of "x" between different
 *                      types of processors.
 *
 *                      The tcl3dGLfloatTo2Byte function discards some of
 *                      the least significant bits of "x" in order to produce
 *                      a more compact representation of "x" than
 *                      tcl3dGLfloatToByte.
 *
 *                      Conversion Function     Precision of Mantissa
 *                      -------------------------------------------------
 *                      tcl3dGLfloatToByte              23 bits
 *                      tcl3dGLfloatTo3Byte             15 bits
 *                      tcl3dGLfloatTo2Byte              7 bits
 *
 *      Return value:   None.
 *
 *      See also:       tcl3dGLfloatToByte
 *                      tcl3dGLfloatTo3Byte
 *
 ***************************************************************************/

void tcl3dGLfloatTo2Byte (GLfloat x, GLbyte buf[2])
{
    double      m;
    int         exponent;
    GLint       mantissa;

    m = frexp ((double)x, &exponent);
    exponent += 126;

    if (m == 0.0 || exponent < 0) {
        buf[0] = 0;
        buf[1] = 0;
    } else {
        mantissa = (GLint)(UT_ABS(m) * (double)(1 << 24));
        buf[0] = (GLbyte)((m >= 0.0? 0x00 : 0x80) | ((exponent >> 1) & 0x7f));
        buf[1] = (GLbyte)((exponent << 7) | ((mantissa >> 16) & 0x7f));
    }

    return;
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dGLdoubleToByte
 *
 *      Usage:          Convert a GLdouble into a 8-Byte vector.
 *
 *      Synopsis:       void tcl3dGLdoubleToByte (GLdouble x, GLbyte buf[8])
 *
 *      Description:    "Buf" is filled with a machine-independent
 *                      representation of "x" (IEEE double-precision
 *                      normalized number), which can be used to
 *                      transfer the value of "x" between different
 *                      types of processors.
 *
 *      Return value:   None.
 *
 *      See also:
 *
 ***************************************************************************/

void tcl3dGLdoubleToByte (GLdouble x, GLbyte buf[8])
{
    #if !defined (USE_EXACTCONV)
        if (tcl3dIsIntel ()) {
            buf[7] = ((GLbyte *)&x)[0];
            buf[6] = ((GLbyte *)&x)[1];
            buf[5] = ((GLbyte *)&x)[2];
            buf[4] = ((GLbyte *)&x)[3];
            buf[3] = ((GLbyte *)&x)[4];
            buf[2] = ((GLbyte *)&x)[5];
            buf[1] = ((GLbyte *)&x)[6];
            buf[0] = ((GLbyte *)&x)[7];
        } else {
            buf[0] = ((GLbyte *)&x)[0];
            buf[1] = ((GLbyte *)&x)[1];
            buf[2] = ((GLbyte *)&x)[2];
            buf[3] = ((GLbyte *)&x)[3];
            buf[4] = ((GLbyte *)&x)[4];
            buf[5] = ((GLbyte *)&x)[5];
            buf[6] = ((GLbyte *)&x)[6];
            buf[7] = ((GLbyte *)&x)[7];
        }
        return;

    #else
        double  m, tmp;
        int     exponent, sgn;
        GLint   m1, m2;

        m = frexp ((double)x, &exponent);
        exponent += 1022;

        if (m == 0.0 || exponent < 0) {
            buf[0] = 0;
            buf[1] = 0;
            buf[2] = 0;
            buf[3] = 0;
            buf[4] = 0;
            buf[5] = 0;
            buf[6] = 0;
            buf[7] = 0;
        } else {
            sgn = (m < 0.0);
            tmp = UT_ABS(m) * (double)(1 << 29);
            m2 = (GLint)tmp;
            m1 = (GLint)((tmp - (double)m2) * (double)(1 << 24));
            buf[0] = (GLbyte)((sgn << 7) | ((exponent >> 4) & 0x7f));
            buf[1] = (GLbyte)(((exponent << 4) & 0xf0) | ((m2 >> 24) & 0x0f));
            buf[2] = (GLbyte)(m2 >> 16);
            buf[3] = (GLbyte)(m2 >> 8);
            buf[4] = (GLbyte)(m2);
            buf[5] = (GLbyte)(m1 >> 16);
            buf[6] = (GLbyte)(m1 >> 8);
            buf[7] = (GLbyte)(m1);
        }
        return;
    #endif
}

GLbyte tcl3dByteToGLbyte (const GLbyte buf[1]) 
{
    return buf[0];
}

GLubyte tcl3dByteToGLubyte (const GLbyte buf[1]) 
{
    return buf[0];
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dByteToGLshort
 *
 *      Usage:          Convert a Byte vector into a GLshort.
 *
 *      Synopsis:       GLshort tcl3dByteToGLshort (const GLbyte buf[2])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of a GLshort number as produced by tcl3dGLshortToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

GLshort tcl3dByteToGLshort (const GLbyte buf[2])
{
    return  (buf[1] & 0x00ff) |
            (buf[0] << 8);
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dByteToGLushort
 *
 *      Usage:          Convert a Byte vector into a GLushort.
 *
 *      Synopsis:       GLushort tcl3dByteToGLushort (const GLbyte buf[2])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of a GLushort number as produced by tcl3dGLushortToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

GLushort tcl3dByteToGLushort (const GLbyte buf[2])
{
    return  (buf[1] & 0x00ff) |
            (buf[0] << 8);
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dByteToGLint
 *
 *      Usage:          Convert a Byte vector into an GLint.
 *
 *      Synopsis:       GLint tcl3dByteToGLint (const GLbyte buf[4])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of an GLint number produced by tcl3dGLintToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

GLint tcl3dByteToGLint (const GLbyte buf[4])
{
    return   (buf[3] & 0x000000ff) |
            ((buf[2] << 8) & 0x0000ff00) |
            ((buf[1] << 16) & 0x00ff0000) |
             (buf[0] << 24);
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dByteToGLuint
 *
 *      Usage:          Convert a Byte vector into an GLuint.
 *
 *      Synopsis:       GLuint tcl3dByteToGLuint (const GLbyte buf[4])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of an GLuint number produced by tcl3dGLuintToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

GLuint tcl3dByteToGLuint (const GLbyte buf[4])
{
    return   (buf[3] & 0x000000ff) |
            ((buf[2] << 8) & 0x0000ff00) |
            ((buf[1] << 16) & 0x00ff0000) |
             (buf[0] << 24);
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dByteToGLfloat
 *
 *      Usage:          Convert a 4-Byte vector into a GLfloat.
 *
 *      Synopsis:       GLfloat tcl3dByteToGLfloat (const GLbyte buf[4])
 *
 *      Description:    For tcl3dByteToGLfloat, "buf" must be the machine-
 *                      independent representation of a GLfloat number as
 *                      produced by tcl3dGLfloatToByte.  The machine's native
 *                      representation of the number is returned.
 *
 *      Return value:   See above.
 *
 *      See also:       tcl3d3ByteToGLfloat
 *                      tcl3d2ByteToGLfloat
 *
 ***************************************************************************/

GLfloat tcl3dByteToGLfloat (const GLbyte buf[4])
{
    #if !defined (USE_EXACTCONV)
        GLfloat m;

        if (tcl3dIsIntel ()) {
            ((GLubyte *)&m)[0] = buf[3];
            ((GLubyte *)&m)[1] = buf[2];
            ((GLubyte *)&m)[2] = buf[1];
            ((GLubyte *)&m)[3] = buf[0];
        } else {
            ((GLubyte *)&m)[0] = buf[0];
            ((GLubyte *)&m)[1] = buf[1];
            ((GLubyte *)&m)[2] = buf[2];
            ((GLubyte *)&m)[3] = buf[3];
        }
        return m;

    #else
        double  m;
        int     exponent, mantissa;

        mantissa =
                ((buf[1] << 16) & 0x007f0000) |
                ((buf[2] << 8)  & 0x0000ff00) |
                 (buf[3]        & 0x000000ff);
        exponent =
                ((buf[0] << 1)  & 0x000000fe) |
                ((buf[1] >> 7)  & 0x00000001);

        if (mantissa == 0 && exponent == 0)
            return 0.0;

        m = 1.0 + (double)mantissa * (1.0 / (double)(1 << 23));
        return (GLfloat) (ldexp (m, exponent-127) * (buf[0] & 0x80? -1.0: 1.0));
    #endif
}

/***************************************************************************
 *[@e
 *      Name:           tcl3d3ByteToGLfloat
 *
 *      Usage:          Convert a 3-Byte vector into a GLfloat.
 *
 *      Synopsis:       GLfloat tcl3d3ByteToGLfloat (const GLbyte buf[3])
 *
 *      Description:    For tcl3d3ByteToGLfloat, "buf"
 *                      must be the machine-independent representation of
 *                      a GLfloat number as produced by tcl3dGLfloatTo3Byte. 
 *                      The machine's native representation of the number,
 *                      with the least significant bits of the mantissa set
 *                      to zero, is returned.
 *
 *      Return value:   See above.
 *
 *      See also:       tcl3dByteToGLfloat
 *                      tcl3d2ByteToGLfloat
 *
 ***************************************************************************/

GLfloat tcl3d3ByteToGLfloat (const GLbyte buf[3])
{
    double      m;
    int exponent, mantissa;

    mantissa =
            ((buf[1] << 16) & 0x007f0000) |
            ((buf[2] << 8)  & 0x0000ff00);
    exponent =
            ((buf[0] << 1)  & 0x000000fe) |
            ((buf[1] >> 7)  & 0x00000001);

    if (mantissa == 0 && exponent == 0)
        return 0.0;

    m = 1.0 + (double)mantissa * (1.0 / (double)(1 << 23));
    return (GLfloat) (ldexp (m, exponent-127) * (buf[0] & 0x80? -1.0: 1.0));
}

/***************************************************************************
 *[@e
 *      Name:           tcl3d2ByteToGLfloat
 *
 *      Usage:          Convert a 2-Byte vector into a GLfloat.
 *
 *      Synopsis:       GLfloat tcl3d2ByteToGLfloat (const GLbyte buf[2])
 *
 *      Description:    For tcl3d2ByteToGLfloat, "buf" must be the
 *                      machine-independent representation of a GLfloat number
 *                      as produced by tcl3dGLfloatTo2Byte.
 *                      The machine's native representation of the number,
 *                      with the least significant bits of the mantissa set
 *                      to zero, is returned.
 *
 *      Return value:   See above.
 *
 *      See also:       tcl3dByteToGLfloat
 *                      tcl3d3ByteToGLfloat
 *
 ***************************************************************************/

GLfloat tcl3d2ByteToGLfloat (const GLbyte buf[2])
{
    double      m;
    int exponent, mantissa;

    mantissa =
             (buf[1] << 16) & 0x007f0000;
    exponent =
            ((buf[0] << 1)  & 0x000000fe) |
            ((buf[1] >> 7)  & 0x00000001);

    if (mantissa == 0 && exponent == 0)
        return 0.0;

    m = 1.0 + (double)mantissa * (1.0 / (double)(1 << 23));
    return (GLfloat) (ldexp (m, exponent-127) * (buf[0] & 0x80? -1.0: 1.0));
}

/***************************************************************************
 *[@e
 *      Name:           tcl3dByteToGLdouble
 *
 *      Usage:          Convert a 8-Byte vector into a GLdouble.
 *
 *      Synopsis:       GLdouble tcl3dByteToGLdouble (const GLbyte buf[8])
 *
 *      Description:    "Buf" must be the machine-independent representation
 *                      of a GLdouble number produced by tcl3dGLdoubleToByte.
 *
 *      Return value:   The machine's native representation of the number is
 *                      returned.
 *
 *      See also:
 *
 ***************************************************************************/

GLdouble tcl3dByteToGLdouble (const GLbyte buf[8])
{
    #if !defined (USE_EXACTCONV)
        GLdouble m;

        if (tcl3dIsIntel ()) {
            ((GLubyte *)&m)[0] = buf[7];
            ((GLubyte *)&m)[1] = buf[6];
            ((GLubyte *)&m)[2] = buf[5];
            ((GLubyte *)&m)[3] = buf[4];
            ((GLubyte *)&m)[4] = buf[3];
            ((GLubyte *)&m)[5] = buf[2];
            ((GLubyte *)&m)[6] = buf[1];
            ((GLubyte *)&m)[7] = buf[0];
        } else {
            ((GLubyte *)&m)[0] = buf[0];
            ((GLubyte *)&m)[1] = buf[1];
            ((GLubyte *)&m)[2] = buf[2];
            ((GLubyte *)&m)[3] = buf[3];
            ((GLubyte *)&m)[4] = buf[4];
            ((GLubyte *)&m)[5] = buf[5];
            ((GLubyte *)&m)[6] = buf[6];
            ((GLubyte *)&m)[7] = buf[7];
        }
        return m;

    #else
        double  m;
        int     exponent, m1, m2;

        m1 =    ((buf[5] << 16) & 0x00ff0000) |
                ((buf[6] << 8)  & 0x0000ff00) |
                 (buf[7]        & 0x000000ff);
        m2 =    ((buf[1] << 24) & 0x0f000000) |
                ((buf[2] << 16) & 0x00ff0000) |
                ((buf[3] << 8)  & 0x0000ff00) |
                 (buf[4]        & 0x000000ff);
        exponent =
                ((buf[0] << 4)  & 0x000007f0) |
                ((buf[1] >> 4)  & 0x0000000f);

        if (m1 == 0 && m2 == 0 && exponent == 0)
            return 0.0;

        m =     (1.0 / ((double)(1 << 28) * (double)(1 << 24))) *
                (((double)(1 << 28) * (double)(1 << 24)) +
                (double)m2 * (double)(1 << 24) +
                (double)m1);
        return  ldexp (m, exponent - 1023) * (buf[0] & 0x80? -1.0: 1.0);
    #endif
}
