//+--------------------------------------------------------------
//
//  For conditions of distribution and use, see copyright notice
//  in Flashpix.h 
// 
//  Copyright (c) 1999 Digital Imaging Group, Inc.
// 
//  Contents: DRT support routines
//
//---------------------------------------------------------------

#include "headers.cxx"

#include <stdarg.h>
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#else
#include "winio.hxx"
#endif

#define DEFAULT_DATA_DIR "."

BOOL fExitOnFail = TRUE;

char szOrigDir[_MAX_PATH] = ".";

// Preserve the current directory and change
// directory into the data directory
void SetData(void)
{
    char *pszDataDir;

    _getcwd(szOrigDir, _MAX_PATH);
    pszDataDir = getenv("DRTDATA");
    if (pszDataDir == NULL)
  pszDataDir = DEFAULT_DATA_DIR;
    _chdir(pszDataDir);
}

// Clean up the data directory
void CleanData(void)
{
    _unlink(pszDRTDF);
}

// Restore the original directory
void UnsetData(void)
{
    _chdir(szOrigDir);
}

// Output a message if fVerbose is true
void out(char *fmt, ...)
{
    va_list args;

    if (fVerbose)
    {
  va_start(args, fmt);
  vprintf(fmt, args);
  va_end(args);
    }
}

//  internal error print
void _errorprint (char *fmt, va_list args)
{
    printf("** Fatal error **: ");
    vprintf(fmt, args);
}

//  error print
void errorprint (char *fmt, ...)
{
    va_list args;

    va_start (args, fmt);

    _errorprint (fmt, args);

    va_end (args);
}

// Print out an error message and terminate the DRT
void error(int code, char *fmt, ...)
{
    va_list args;

    va_start(args, fmt);

    _errorprint (fmt, args);

    va_end(args);
    CleanData();
    UnsetData();
    exit(code);
}


// Converts a TCHAR string to a char pointer in a temporary buffer
// This implementation treats the conversion buffer as a circular
// buffer so more than one string can be held (depending on the size
// of the strings)

#define BUFSIZE 1024

char *OlecsOut(OLECHAR const *ptcs)
{
#ifdef OLEWIDECHAR
    static char szBuffer[BUFSIZE];
    static char *pszBuf = szBuffer;
    char *pszTmp;

    if (ptcs == NULL)
        return NULL;
    if (wcslen(ptcs) >= (size_t)(BUFSIZE-(pszBuf-szBuffer)))
        pszBuf = szBuffer;
    OLETOA(ptcs, pszBuf, BUFSIZE);
    szBuffer[BUFSIZE-1] = 0;
    pszTmp = pszBuf;
    pszBuf += strlen(pszBuf)+1;
    return pszTmp;
#else
    return (char *)ptcs;
#endif
}

typedef struct
{
    SCODE sc;
    char *text;
} StatusCodeText;

static StatusCodeText scodes[] =
{
    S_OK, "S_OK",
    S_FALSE, "S_FALSE",
    STG_E_INVALIDFUNCTION, "STG_E_INVALIDFUNCTION",
    STG_E_FILENOTFOUND, "STG_E_FILENOTFOUND",
    STG_E_PATHNOTFOUND, "STG_E_PATHNOTFOUND",
    STG_E_TOOMANYOPENFILES, "STG_E_TOOMANYOPENFILES",
    STG_E_ACCESSDENIED, "STG_E_ACCESSDENIED",
    STG_E_INVALIDHANDLE, "STG_E_INVALIDHANDLE",
    STG_E_INSUFFICIENTMEMORY, "STG_E_INSUFFICIENTMEMORY",
    STG_E_INVALIDPOINTER, "STG_E_INVALIDPOINTER",
    STG_E_NOMOREFILES, "STG_E_NOMOREFILES",
    STG_E_DISKISWRITEPROTECTED, "STG_E_DISKISWRITEPROTECTED",
    STG_E_SEEKERROR, "STG_E_SEEKERROR",
    STG_E_WRITEFAULT, "STG_E_WRITEFAULT",
    STG_E_READFAULT, "STG_E_READFAULT",
    STG_E_SHAREVIOLATION, "STG_E_SHAREVIOLATION",
    STG_E_LOCKVIOLATION, "STG_E_LOCKVIOLATION",
    STG_E_FILEALREADYEXISTS, "STG_E_FILEALREADYEXISTS",
    STG_E_INVALIDPARAMETER, "STG_E_INVALIDPARAMETER",
    STG_E_MEDIUMFULL, "STG_E_MEDIUMFULL",
    STG_E_ABNORMALAPIEXIT, "STG_E_ABNORMALAPIEXIT",
    STG_E_INVALIDHEADER, "STG_E_INVALIDHEADER",
    STG_E_INVALIDNAME, "STG_E_INVALIDNAME",
    STG_E_UNKNOWN, "STG_E_UNKNOWN",
    STG_E_UNIMPLEMENTEDFUNCTION, "STG_E_UNIMPLEMENTEDFUNCTION",
    STG_E_INVALIDFLAG, "STG_E_INVALIDFLAG",
    STG_E_INUSE, "STG_E_INUSE",
    STG_E_NOTCURRENT, "STG_E_NOTCURRENT",
    STG_E_REVERTED, "STG_E_REVERTED",
    STG_E_CANTSAVE, "STG_E_CANTSAVE",
    STG_E_OLDFORMAT, "STG_E_OLDFORMAT",
    STG_E_OLDDLL, "STG_E_OLDDLL",
    STG_E_SHAREREQUIRED, "STG_E_SHAREREQUIRED",
    STG_E_NOTFILEBASEDSTORAGE, "STG_E_NOTFILEBASEDSTORAGE",
    STG_S_CONVERTED, "STG_S_CONVERTED"
};
#define NSCODETEXT (sizeof(scodes)/sizeof(scodes[0]))

// Convert a status code to text
char *ScText(SCODE sc)
{
    int i;

    for (i = 0; i<NSCODETEXT; i++)
  if (scodes[i].sc == sc)
      return scodes[i].text;
    return "?";
}

// Output a call result and check for failure
HRESULT Result(HRESULT hr)
{
    SCODE sc;

    sc = DfGetScode(hr);
    out(" - %s (0x%lX)\n", ScText(sc), sc);
    if (FAILED(sc) && fExitOnFail)
        error(EXIT_BADSC, "Unexpected call failure\n");
    return hr;
}

// Perform Result() when the expectation is failure
HRESULT IllResult(char *pszText, HRESULT hr)
{
    SCODE sc;

    sc = DfGetScode(hr);
    out("%s - %s (0x%lX)\n", pszText, ScText(sc), sc);
    if (SUCCEEDED(sc) && fExitOnFail)
        error(EXIT_BADSC, "Unexpected call success\n");
    return hr;
}

// Check whether a given storage has a certain
// structure or not
// Structure is given as a string with elements:
//   <Type><Name><Options>[,...]
//   Type - d for docfile and s for stream
//   Name - Characters up to a '(' or ','
//   Options - For a docfile, you can specify a recursive check
//     in parentheses
//
// Example:  dDocfile(sStream,dDocfile)
char *VerifyStructure(IStorage *pstg, char *pszStructure)
{
    char szName[CWCSTORAGENAME], *psz;
    IStorage *pstgChild;
    char chType;
    SCODE sc;
    CStrList sl;
    SStrEntry *pse;
    IEnumSTATSTG *penm;
    STATSTG stat;
    OLECHAR atcName[CWCSTORAGENAME];

    if (FAILED(sc = DfGetScode(pstg->EnumElements(0, NULL, 0, &penm))))
  error(EXIT_BADSC, "VerifyStructure: Unable to create enumerator - "
        "%s (0x%lX)\n", ScText(sc), sc);
    for (;;)
    {
  sc = DfGetScode(penm->Next(1, &stat, NULL));
  if (sc == S_FALSE)
      break;
  else if (FAILED(sc))
      error(EXIT_BADSC, "VerifyStructure: Unable to enumerate - "
        "%s (0x%lX)\n", ScText(sc), sc);
  pse = sl.Add(stat.pwcsName);
  if (pse == NULL)
      error(EXIT_OOM, "VerifyStructure: Unable to allocate string\n");
  pse->user.dw = stat.type;
  drtMemFree(stat.pwcsName);
    }
    penm->Release();
    while (*pszStructure && *pszStructure != ')')
    {
  chType = *pszStructure++;
  psz = szName;
  while (*pszStructure && *pszStructure != '(' &&
         *pszStructure != ')' && *pszStructure != ',')
      *psz++ = *pszStructure++;
  *psz = 0;
        ATOOLE(szName, atcName, CWCSTORAGENAME);
  pse = sl.Find(atcName);
  if (pse == NULL)
      error(EXIT_BADSC, "VerifyStructure: '%s' not found\n", szName);
  switch(chType)
  {
  case 'd':
      if (pse->user.dw != STGTY_STORAGE)
    error(EXIT_BADSC, "VerifyStructure: '%s' is not a storage\n",
          szName);
      sc = DfGetScode(pstg->OpenStorage(atcName, NULL,
                                              STGP(STGM_READWRITE), NULL,
                                              0, &pstgChild));
      if (FAILED(sc))
    error(EXIT_BADSC, "VerifyStructure: can't open storage "
          "'%s' - %s\n", szName, ScText(sc));
      if (*pszStructure == '(')
    pszStructure = VerifyStructure(pstgChild, pszStructure+1)+1;
      pstgChild->Release();
      break;
  case 's':
      if (pse->user.dw != STGTY_STREAM)
    error(EXIT_BADSC, "VerifyStructure: '%s' is not a stream\n",
          szName);
      break;
  }
  sl.Remove(pse);
  if (*pszStructure == ',')
      pszStructure++;
    }
    for (pse = sl.GetHead(); pse; pse = pse->pseNext)
  error(EXIT_BADSC, "VerifyStructure: additional member '%s'\n",
        OlecsOut(pse->atc));
    return pszStructure;
}

// Creates a structure using the same syntax
// as VerifyStructure
char *CreateStructure(IStorage *pstg, char *pszStructure)
{
    char szName[CWCSTORAGENAME], *psz;
    IStorage *pstgChild;
    IStream *pstmChild;
    char chType;
    SCODE sc;
    OLECHAR atcName[CWCSTORAGENAME];

    while (*pszStructure && *pszStructure != ')')
    {
  chType = *pszStructure++;
  psz = szName;
  while (*pszStructure && *pszStructure != '(' &&
         *pszStructure != ')' && *pszStructure != ',')
      *psz++ = *pszStructure++;
  *psz = 0;
        ATOOLE(szName, atcName, CWCSTORAGENAME);
  switch(chType)
  {
  case 'd':
      sc = DfGetScode(pstg->CreateStorage(atcName, STGP(STGM_READWRITE),
                                                0, 0, &pstgChild));
      if (FAILED(sc))
    error(EXIT_BADSC, "CreateStructure: can't create storage "
          "'%s' - %s\n", szName, ScText(sc));
      if (*pszStructure == '(')
    pszStructure = CreateStructure(pstgChild, pszStructure+1)+1;
      pstgChild->Release();
      break;
  case 's':
      sc = DfGetScode(pstg->CreateStream(atcName, STMP(STGM_READWRITE),
                                               0, 0, &pstmChild));
      if (FAILED(sc))
    error(EXIT_BADSC, "CreateStructure: can't create stream "
          "'%s' - %s\n", szName, ScText(sc));
      pstmChild->Release();
      break;
  }
  if (*pszStructure == ',')
      pszStructure++;
    }
    pstg->Commit(0);
    return pszStructure;
}

// Verifies the fields of a STATSTG
void VerifyStat(STATSTG *pstat, OLECHAR *ptcsName, DWORD type, DWORD grfMode)
{
    if (ptcsName == NULL)
    {
        if (pstat->pwcsName != NULL)
      error(EXIT_BADSC, "Stat name should be NULL - is %p\n",
                  pstat->pwcsName);
    }
    else if (olecscmp(pstat->pwcsName, ptcsName))
  error(EXIT_BADSC, "Stat name mismatch - has '%s' vs. '%s'\n",
        OlecsOut(pstat->pwcsName), OlecsOut(ptcsName));
    if (pstat->type != type)
  error(EXIT_BADSC, "Stat type mismatch - has %lu vs. %lu\n",
        pstat->type, type);
    if (pstat->grfMode != grfMode)
  error(EXIT_BADSC, "Stat mode mismatch - has 0x%lX vs. 0x%lX\n",
        pstat->grfMode, grfMode);
}

// Checks on a file's existence
BOOL Exists(OLECHAR* ocsFile)
{   
    char pszFile[_MAX_PATH];
    TTOS(ocsFile, pszFile, _tcslen(ocsFile)+1);
    FILE *f=fopen(pszFile, "r");
    BOOL fExists= (f!=NULL);
    if (f!=NULL) fclose(f);
    return (fExists);
}

// Gets a file's length
ULONG Length(OLECHAR *ocsFile)
{
    ULONG cb;
    char pszFile[_MAX_PATH];
    TTOS(ocsFile, pszFile, _tcslen(ocsFile)+1);

    FILE *f=fopen(pszFile, "r");
    if (!f) 
  error(EXIT_BADSC, "Length: Unable to open '%s'\n", pszFile);
    int rcode=fseek(f, 0, SEEK_END);
    if (rcode!=0)
  error(EXIT_BADSC, "Length: Unable to get length for '%s'\n",
        pszFile);
    cb= (ULONG) ftell(f);
    fclose(f);

    return cb;
}

// Equality for FILETIME
BOOL IsEqualTime(FILETIME ttTime, FILETIME ttCheck)
{
#ifdef _UNIX
    #ifdef LONGLONG
    #error "LONGLONG should not be defined"
    #else
    #define LONGLONG time_t
    #endif
#endif
    //  File times can be off by as much as 2 seconds due to FAT rounding
    LONGLONG tmTime = *(LONGLONG *)&ttTime;
    LONGLONG tmCheck = *(LONGLONG *)&ttCheck;
    LONGLONG tmDelta = tmTime - tmCheck;
#ifdef _UNIX
    #undef LONGLONG
#endif
#ifndef _UNIX
    return tmDelta < 20000000i64 && tmDelta > -2i64 ;
#else
    return tmDelta < 2; // time_t is in seconds
#endif
}

// Get a fully qualified path for a file name
void GetFullPath(TCHAR *ocsFile, TCHAR *ocsPath, int len)
{
    char pszPath[_MAX_PATH+1], pszFile[_MAX_PATH+1];
    TTOS(ocsFile, pszFile, _tcslen(ocsFile)+1);
    _fullpath(pszPath, pszFile, len);
    STOT(pszPath, ocsPath, strlen(pszPath)+1);
}

//  Memory helper functions

HRESULT drtMemAlloc(ULONG ulcb, void **ppv)
{
    HRESULT hr;
    IMalloc *pMalloc = NULL;

    if (SUCCEEDED(DfGetScode(hr = CoGetMalloc(MEMCTX_TASK, &pMalloc))))
    {
        *ppv = pMalloc->Alloc(ulcb);
        pMalloc->Release();

        if (*ppv == NULL)
            return ResultFromScode(E_OUTOFMEMORY);
    }

    return hr;
}

void drtMemFree(void *pv)
{
    IMalloc FAR* pMalloc;
    if (SUCCEEDED(GetScode(CoGetMalloc(MEMCTX_TASK, &pMalloc))))
    {
        pMalloc->Free(pv);
        pMalloc->Release();
    }
}

#ifdef _MSC_VER
#pragma pack(1)
#endif

struct SplitGuid
{
    DWORD dw1;
    WORD w1;
    WORD w2;
    BYTE b[8];
};

#ifdef _MSC_VER
#pragma pack()
#endif

char *GuidText(GUID const *pguid)
{
    static char buf[39];
    SplitGuid *psg = (SplitGuid *)pguid;

    sprintf(buf, "{%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}",
            psg->dw1, psg->w1, psg->w2, psg->b[0], psg->b[1], psg->b[2],
            psg->b[3], psg->b[4], psg->b[5], psg->b[6], psg->b[7]);
    return buf;
}
