// (c) 1994-2007 Electronic Arts Inc.

/*------------------------------------------------------------------*/
/*                                                                  */
/*           Gimex Standard File and Memory Interface v1.28         */
/*                                                                  */
/*                     by FrANK G. Barchard, EAC                    */
/*                                                                  */
/*                     Code Module - Oct 11, 2001                   */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Version Date   SE History:                                       */
/* ------- ----   -- --------                                       */
/*    1.xx xxxxxx FB No version history available                   */
/*    1.06 941225 FB Debugged final version                         */
/*    1.07 941225 GS Fixed bug in gopen                             */
/*    1.08 950524 FB r+b and w+b for read/write access              */
/*    1.09 950529 GS clear memory on galloc/gfree (#ifdef _DEBUG)   */
/*    1.10 950613 FB bumped to 100 megs for SGI huge pix            */
/*    1.11 951102 FB con: for stdin and stdout                      */
/*    1.12 961022 FB seek and write                                 */
/*    1.13 961114 FB stdgimex.h                                     */
/*    1.14 970109 FB gread eof set endptr                           */
/*    1.15 970227 GS check for null, pass lint check                */
/*    1.16 970828 KO added GCALL                                    */
/*    1.17 971209 KO gopen opens with "rb" not "r+b"                */
/*    1.18 980124 SK implemented caching in all file io functions   */
/*    1.19 980128 SK added isDirectory, isReadOnly for error checks */
/*    1.20 981116 FB gwopen use temporary file                      */
/*    1.21 000623 FB avoid fwrite of 0 bytes                        */
/*    1.22 001121 FB 64 bit file io                                 */
/*    1.23 001123 FB lseeki64 on windows                            */
/*    1.24 001204 FB perror on read/write errors                    */
/*    1.25 001204 FB if lseek64 fails, try lseek. stdgimex.h protos */
/*    1.26 001221 FB galloc revert to old version with sentinels    */
/*    1.27 010222 FB VC6 header bug fix - struct _RPC_ASYNC_STATE   */
/*    1.28 011011 FB convert / to \ on pc                           */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Module Notes:                                                    */
/* -------------                                                    */
/* This module uses unbuffered unix IO with 64 bit extensions for   */
/* gseek, gtell and glen                                            */
/*                                                                  */
/*------------------------------------------------------------------*/

#define __NOINLINE__ 1
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if defined(_MSC_VER)
// VC6 header bug fix
struct _RPC_ASYNC_STATE;
#include <windows.h>
#include <io.h>
#else
#include <unistd.h>
#endif
#include "stdgimex.h"
#include "stdgimex.h"

/* Memory Functions */

int galloccount=0;
int gopenmethod=3;  /* 0=con:, 1=fopen, 2=backup, 3=temp */

//%lld -> %I64d
int __cdecl stdprintf(const char *format, ...)
{
    va_list arglist;
    int r;
    char format2[8192];
    char tempstr[8192];
    const char *s=format;
    char *d=format2;
    while (*s)
    {
        if (!strncmp(s,"%lld",4))
        {
            strcpy(d,"%I64d");
            d += 5;
            s += 4;
        }
        else
            *d++ = *s++;
    }
    *d = 0;
    va_start(arglist, format);
#if defined (_MSC_VER)
    r = _vsnprintf(tempstr, 8192, format2, arglist);
#else
    r = vsprintf(tempstr, format2, arglist);
#endif
    printf("%s",tempstr);
    va_end(arglist);
    return(r);
}

int __cdecl (*gprintf)(const char *, ...) = stdprintf;

void * GCALL galloc(int size)
{
    char *memptr;

    if (size<=0)
    {
        gprintf("galloc - INVALID SIZE %ld\n",size);
        return((void *)0);
    }
#ifdef DEBUG
    if (size>=40*1024*1024)
    {
        gprintf("galloc - WARNING VERY LARGE SIZE %ld\n",size);
    }
#endif

    // diagnostic for regression testing gimex modules/tools
    {
        static int gallocmax=0;
        if (!gallocmax)
        {
            char *forcedriver;
            forcedriver = getenv("GALLOCMAX");
            if (forcedriver)
                gallocmax = atoi(forcedriver);
            else
                gallocmax = -1;
        }
        if (galloccount==gallocmax)
        {
            gprintf("galloc - ERROR, PURPOSELY FAILING ON GALLOC COUNT %d, SIZE %ld\n",galloccount,size);
            return((void *)0);
        }

    }

    memptr = (char*) malloc((size_t)size+12);
    if (!memptr || memptr==(char *)-1)
    {
        gprintf("galloc - FAILED. OUT OF MEMORY. SIZE REQUESTED %ld\n",size);
        return((void *)0);
    }

    memcpy(memptr,"GMEM",4);            /* sentinal */
    *(int *) ((char *)memptr+4) = size;
    memcpy(memptr+8+size,"GEND",4);

#ifdef _DEBUG
    /* make initial data garbage to bring out errors */
    memset(memptr+8, 0x66, size);
#endif
    ++galloccount;
    return((void *)(memptr+8));
}

bool GCALL gfree(void *memptr)
{
    int size;
    bool ok=false;

    if ((int)memptr>-2048 && (int)memptr<2048)
    {
        gprintf("gfree - ATTEMPT TO FREE NULL POINTER\n");
    }
    else
    {
        size = *(int *) ((char *) memptr - 4);

        if (!memcmp(((char *) memptr-8),"GMEM",4) && size>0)
            if (!memcmp(((char *) memptr+size),"GEND",4))
                ok = true;
        if (!ok)
        {
            if (!memcmp((char *) memptr-8,"GFRE",4))
                gprintf("gfree - ATTEMPT TO FREE MEMORY TWICE\n");
            else
                gprintf("gfree - SENTINAL CORRUPTED; PROBABLE MEMORY DATA CORRUPTION\n");
        }
        else
        {
            memcpy((char *) memptr - 8,"GFRE",4);            /* mark as freed */
#ifdef _DEBUG
            /* make data garbage to bring out errors */
            memset(memptr, 'x', size);
#endif
            free((char *) memptr - 8);

            --galloccount;
        }
    }

    return(ok);
}

/* File Functions */

GSTREAM * GCALL gopen(const char *filename)
{
#if defined(_MSC_VER)
    char namebuf[512];
    strcpy(namebuf,filename);
    for (int i=0; namebuf[i]; ++i)
        if (namebuf[i]=='/') namebuf[i] = '\\';
    int h=open(namebuf, O_RDONLY|O_BINARY);
#elif defined(O_LARGEFILE) && defined(__unix__) && (defined(__i386__) || defined(__mips) || defined(sparc))
    int h=open64(filename, O_RDONLY|O_LARGEFILE);
#elif defined(__unix__) && (defined(__i386__) || defined(__mips) || defined(sparc))
    int h=open64(filename, O_RDONLY);
#else
    int h=open(filename, O_RDONLY);
#endif
    if (h<0) h = 0;
    return((GSTREAM *) h);
}

//#elif defined(O_LARGEFILE)
//int h=open(filename, O_WRONLY|O_CREAT|O_LARGEFILE, S_IRWXU|S_IRGRP|S_IROTH);

GSTREAM * GCALL gwopen(const char *filename)
{
#if defined(_MSC_VER)
    char namebuf[512];
    strcpy(namebuf,filename);
    for (int i=0; namebuf[i]; ++i)
        if (namebuf[i]=='/') namebuf[i] = '\\';
    int h=open(namebuf, O_RDWR|O_CREAT|O_BINARY|O_TRUNC, _S_IREAD|_S_IWRITE);
#elif defined(O_LARGEFILE) && defined(__unix__) && (defined(__i386__) || defined(__mips) || defined(sparc))
    int h=open64(filename, O_RDWR|O_CREAT|O_TRUNC|O_LARGEFILE, S_IRWXU|S_IRGRP|S_IROTH);
#elif defined(__unix__) && (defined(__i386__) || defined(__mips) || defined(sparc))
    int h=open64(filename, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU|S_IRGRP|S_IROTH);
#else
    int h=open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU|S_IRGRP|S_IROTH);
#endif
    if (h<0) h = 0;
    return((GSTREAM *) h);
}

bool GCALL gclose(GSTREAM *g)
{
    bool ok=true;
    if (g)
        ok = !close((int) g);
    return ok;
}

int GCALL gread(GSTREAM *g, void *buf, int size)
{
    int amountread = read((int) g, buf, (unsigned int) size);
    if (amountread < size)
    {
#if defined(_MSC_VER)
#ifdef DEBUG
        int err;
        err = GetLastError();
        gprintf("read warning.  %d requested, got %d. Error %d.  gtell %lld\n", size, amountread, err, gtell(g));
#endif
        if (amountread==0)
            amountread = read((int) g, buf, (unsigned int) size);

#ifdef DEBUG
        gprintf("reread.  %d requested, got %d. Error %d.  gtell %lld\n", size, amountread, err, gtell(g));
#endif
#endif
#ifdef DEBUG
        perror("read warning -");
#endif
    }
    return(amountread);
}

int GCALL gwrite(GSTREAM *g, const void *buf, int size)
{
    int amountwritten = write((int) g, buf, (unsigned int) size);
    if (amountwritten < size)
    {
        gprintf("write warning.  %d requested, got %d\n", size, amountwritten);
#ifdef DEBUG
        perror("write warning-");
#endif
    }
    return(amountwritten);
}

static GPOS __gseek(GSTREAM *g, GPOS seekpos, int whence)
{
    GPOS pos;
#if defined(_MSC_VER)
    pos = (GPOS) _lseeki64((int) g, (__int64)seekpos, whence);
#elif defined(__unix__) && (defined(__i386__) || defined(__mips) || defined(sparc))
    pos=lseek64((int) g, seekpos, whence);
#else // osx beos
    pos=lseek((int) g, (int)seekpos, whence);
#endif
    // retry with near seek
    if (pos==(GPOS)-1)
    {
#ifdef DEBUG
        gprintf("__gseek warning.  pos attempted %lld whence %d\n", seekpos, whence);
#endif
        pos = (GPOS) lseek((int) g, (int) seekpos, whence);
#ifdef DEBUG
        gprintf("__gseek reseek returned %lld\n", pos);
#endif
    }

#ifdef DEBUG
    if (pos==(GPOS)-1)
        perror("__gseek -");
#endif
    return(pos);
}

bool GCALL gseek(GSTREAM *g, GPOS seekpos)
{
    bool ok=true;
    GPOS newpos;

    newpos = __gseek(g,seekpos,SEEK_SET);
    if (newpos==(GPOS)-1)
        ok = false;

    return ok;
}

GPOS GCALL glen(GSTREAM *g)
{
#if defined(_MSC_VER)
    return((GPOS)_filelengthi64((int) g));
#else

    GPOS oldpos;
    GPOS len;

    oldpos = __gseek(g,(GPOS) 0,SEEK_CUR);

    len = __gseek(g,(GPOS) 0,SEEK_END);

    oldpos = __gseek(g,oldpos,SEEK_SET);
    if (len==(GPOS)-1)
        len = (GPOS) 0;

    return(len);
#endif
}

GPOS GCALL gtell(GSTREAM *g)
{
#if defined(_MSC_VER)
    return((GPOS)_telli64((int) g));
#else

    GPOS currentpos;

    currentpos = __gseek(g,(GPOS) 0,SEEK_CUR);

    return(currentpos);
#endif
}


