// util_io - I/O routines from simh package -- "endian-independent"
// borrowed from scp.c, with this copyright notice:

// use fxread and fxwrite instead of fread and fwrite

/*
   Copyright (c) 1993-2002, Robert M Supnik

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the name of Robert M Supnik shall not
   be used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.
*/

/* Endian independent binary I/O package

   For consistency, all binary data read and written by the simulator
   is stored in little endian data order.  That is, in a multi-byte
   data item, the bytes are written out right to left, low order byte
   to high order byte.  On a big endian host, data is read and written
   from high byte to low byte.  Consequently, data written on a little
   endian system must be byte reversed to be usable on a big endian
   system, and vice versa.

   These routines are analogs of the standard C runtime routines
   fread and fwrite.  If the host is little endian, or the data items
   are size char, then the calls are passed directly to fread or
   fwrite.  Otherwise, these routines perform the necessary byte swaps
   using an intermediate buffer.
*/

#include <stdio.h>
#include "util_io.h"

#define int32       int
#define FLIP_SIZE   1024            /* make the flip buffer smaller for these utilities */

static int sim_end = 1;         /* 1 = little-endian */
static unsigned char sim_flip[FLIP_SIZE];
static int end_tested = 0;

void util_io_init (void)
{
    union {int32 i; char c[sizeof (int32)]; } end_test;

    end_test.i = 1;                     /* test endian-ness */
    sim_end    = end_test.c[0];
    end_tested = 1;
}

size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr)
{
    size_t c, j, nelem, nbuf, lcnt, total;
    int32 i, k;
    unsigned char *sptr, *dptr;

    if (! end_tested)
        util_io_init();

    if (sim_end || (size == sizeof(char)))
        return fread(bptr, size, count, fptr);

    if ((size == 0) || (count == 0))
        return 0;

    nelem = FLIP_SIZE / size;               /* elements in buffer */
    nbuf  = count / nelem;                  /* number buffers */
    lcnt  = count % nelem;                  /* count in last buf */

    if (lcnt)
        ++nbuf;
    else
        lcnt = nelem;

    total = 0;
    dptr  = bptr;                       /* init output ptr */

    for (i = nbuf; i > 0; i--) {
        if ((c = fread(sim_flip, size, (i == 1) ? lcnt : nelem, fptr)) == 0)
            return total;

        total += c;

        for (j = 0, sptr = sim_flip; j < c; j++) {
            for (k = size - 1; k >= 0; k--)
                *(dptr + k) = *sptr++;

            dptr += size;
        }
    }

    return total;
}

size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr)
{
    size_t c, j, nelem, nbuf, lcnt, total;
    int32 i, k;
    unsigned char *sptr, *dptr;

    if (! end_tested)
        util_io_init();

    if (sim_end || (size == sizeof(char)))
        return fwrite(bptr, size, count, fptr);

    if ((size == 0) || (count == 0))
        return 0;

    nelem = FLIP_SIZE / size;               /* elements in buffer */
    nbuf  = count / nelem;                  /* number buffers */
    lcnt  = count % nelem;                  /* count in last buf */

    if (lcnt)
        ++nbuf;
    else
        lcnt = nelem;

    total = 0;
    sptr  = bptr;                           /* init input ptr */

    for (i = nbuf; i > 0; i--) {
        c = (i == 1) ? lcnt : nelem;

        for (j = 0, dptr = sim_flip; j < c; j++) {
            for (k = size - 1; k >= 0; k--)
                *(dptr + k) = *sptr++;

            dptr += size;
        }

        if ((c = fwrite(sim_flip, size, c, fptr)) == 0)
            return total;

        total += c;
    }

    return total;
}
