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

const char TextVersion[]="1.26 (Samba) 2007.12.4";
#define TOOL_VERSION 126
#define TOOL_NAME "venom"

const char *SPIT_history[] =
{
"------------------------------------------------------------------",
"                                                                  ",
"                         Venom v1.26                              ",
"                                                                  ",
"                    by FrANK G. Barchard, EAC                     ",
"                                                                  ",
"                   Main program - May 14, 2003                    ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Version Date   SE History:                                       ",
" ------- ----   -- --------                                       ",
" 1.00    010820 FB based on author                                ",
" 1.01    010822 FB mw, -format                                    ",
" 1.02    010823 FB icl, -time                                     ",
" 1.03    010825 FB -match                                         ",
" 1.04    010905 FB dll                                            ",
" 1.05    010910 FB interate true code pages (languages) for dlls  ",
" 1.06    010930 FB spit TextVersion= style                        ",
" 1.07    011017 FB fonts                                          ",
" 1.08    011106 FB extended font range                            ",
" 1.09    011106 FB gfn fonts                                      ",
" 1.10    011210 FB eagl _major/_minor/_patch notation             ",
" 1.11    020407 FB big5 version                                   ",
" 1.12    020411 FB SCE_LIBRARY_VERSION                            ",
" 1.13    020416 FB all big formats have venom/flags after toc now ",
" 1.14    020507 FB dll/exe language codepage byte swap            ",
" 1.15    020605 FB freetype _VERSION (eagl2style) with 2 reference",
" 1.16    020617 FB minor dll asis                                 ",
" 1.17    020619 FB ttf                                            ",
" 1.18    020622 FB exe /version:1.23 major/minor in exe header    ",
" 1.19    020627 FB dll first 3 numbers                            ",
" 1.20    021103 FB xbox xtl.h _VER 5120, /version:1.23, -v3 path  ",
" 1.21    021107 FB try dll/exe current folder first, zlib         ",
" 1.22    021129 FB -minor for major.minor                         ",
" 1.23    021206 FB -v1 don't show version not found               ",
" 1.24    030128 FB look for define                                ",
" 1.25    030307 FB gdf packkage .build targetversion=             ",
" 1.26    030514 FB major.minor.patch.build                        ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Module Notes:                                                    ",
" -------------                                                    ",
"                                                                  ",
" realbase.h                                                       ",
" #define REAL_VERSION 500                                         ",
" #define REAL_PATCH   0                                           ",
"                                                                  ",
" gcc --version                                                    ",
" 2.95.3                                                           ",
"                                                                  ",
" >cl                                                              ",
" Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804",
" Copyright (C) Microsoft Corp 1984-1998. All rights reserved.     ",
"                                                                  ",
" snd.h                                                            ",
" #define SND_HEADER_LIB_SYNC         27                           ",
"                                                                  ",
"  P2                                                              ",
" (5.2.9.0)                                                        ",
"                                                                  ",
" Spit tools                                                       ",
" const char TextVersion[]=""1.53c (Samba) 2007.12.4",
" #define TextVersion ""1.53c (Samba) 2007.12.4",
"                                                                  ",
" #define SIMEX_VERSION_MAJOR         7                            ",
" #define SIMEX_VERSION_MINOR         9                            ",
" #define SIMEX_VERSION_PATCH         0                            ",
" #define SIMEX_VERSION ((SIMEX_VERSION_MAJOR << 16)               ",
"         | (SIMEX_VERSION_MINOR << 8) | SIMEX_VERSION_PATCH)      ",
"                                                                  ",
" #define EA3_MAJOR_VERSION 3                                      ",
" #define EA3_MINOR_VERSION 12                                     ",
" #define EA3_INTERM_VERSION 0                                     ",
"                                                                  ",
" GNU Make version 3.77, by Richard Stallman and Roland McGrath.   ",
" Copyright (C) 1988, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98       ",
" 	Free Software Foundation, Inc.                                 ",
"                                                                  ",
" Text v1.53c (Vogue) 010530 by FrANK Barchard, EAC                ",
" USAGE: txt -options [files]                                      ",
"    -pc convert to pc text     -unix convert to unix (SGI)        ",
"                                                                  ",
" real-4.30.12-pc.zip                                              ",
"                                                                  ",
" gx.rc                                                            ",
" FILEVERSION 4,56,0,0                                             ",
"                                                                  ",
" Shapes                                                           ",
" 00000: 53485047 50000200 00000001 47323830  SHPGP.......G280     ",
"                                                                  ",
" Big files ('L218' follows table of contents)                     ",
" 00000: 42494746 85300000 00000001 00000027  BIGF.0.........'     ",
" 00010: 00000028 0000305d 73706974 2e68004c  ...(..0]spit.h.L     ",
" 00020: 32313800 00000000 2f2a2043 6f707972  218...../* Copyr     ",
"                                                                  ",
" Metrowerks C/C++ Compiler for Embedded PowerPC.                  ",
" Copyright (c)1998-2000 Metrowerks, Inc.                          ",
" All rights reserved.                                             ",
" Version 2.3.3 build 159                                          ",
" Runtime Built: Feb  7 2001 12:08:38                              ",
"                                                                  ",
" Please enter 'MWCCEPPC.EXE -help' for information a              ",
"                                                                  ",
"                                                                  ",
" E:\real>mwasmeppc                                                ",
"                                                                  ",
" Metrowerks Assembler for Embedded PowerPC.                       ",
" Copyright (c)1998-1999 Metrowerks, Inc.                          ",
" All rights reserved.                                             ",
" Version 2.3.2 build 106                                          ",
" Runtime Built: Jun  2 2000 15:30:53                              ",
"                                                                  ",
" Please enter 'MWASME~1.EXE -help' for information a              ",
"                                                                  ",
" Tools comment convention                                         ",
" /* Version Date   SE History:                             */     ",
" /* ------- ----   -- --------                             */     ",
" /*    1.00 940414 FB First gimex prototype module         */     ",
" /*    1.10 940425 FB Reimplimented reentrantly            */     ",
"                                                                  ",
" spit tools                                                       ",
" const char TextVersion[]='1.00 (Samba) 2007.12.4",
"                                                                  ",
" Intel(R) C++ Compiler .. applications, Version 5.0 Build 001120  ",
" Copyright (C) 1985-2000 Intel Corporation.  All rights reserved. ",
"                                                                  ",
" fonts                                                            ",
" XDump v1.66 (Freedom) 011010 by FrANK Barchard, EAC              ",
" 00000: 464e5446 304b0000 c8006000 09000000  FNTF0K..H.`.....     ",
"                                                                  ",
" Sony lib versions in libver.h                                    ",
" #define SCE_LIBRARY_VERSION 0x2500 /* Release Library Version */ ",
"                                                                  ",
" File XTL.h:													   ",
" #define _XTL_VER                   5120						   ",
"                                                                  ",
" File pc.mak:                                                     ",
" LINKOPTS=/nologo /version:3.64 /INCREMENTAL:NO /SUBSYSTEM:CO     ",
"                                                                  ",
" File zlib.h:                                                     ",
" #define ZLIB_VERSION \"1.1.4\"								   ",
"                                                                  ",
" File Master.build:                                               ",
" <package name=\"PlayStation2SN\" targetversion=\"2.7.0\"/>       ",
"                                                                  ",
"------------------------------------------------------------------",
"                                                                  ",
" Why Venom:                                                       ",
" ----------                                                       ",
" VE = Version                                                     ",
" NOM = Name (french)                                              ",
" Venom retrieves version numbers from various types of files      ",
" and making them usable in automated processes                    ",
" -build systems that want to assert version numbers               ",
" -renaming files by version number                                ",
" Its a Spit tool                                                  ",
"                                                                  ",
" Did you know Ants and Cobras spit venom?                         ",
"                                                                  ",
"------------------------------------------------------------------",
"\0"
};

#if 0
/*
;
; ABSTRACT
;
; venom - outputs version number
;
; SUMMARY
;
; venom <inputfile>
;    @file     response file, containing a list of options and files
;    =file     write to a file
;    -p<str>   prefix string
;    -s<str>   suffix string
;    -n<str>   name of variable (REALvenom default)
;    -a<str>   venom string
;    -return<expr> return expression (default major*100+minor)
;              variables available: major, minor, patch, build, size
;    -nologo   disable banner
;    -v0       no console output
;    -v1       prints source file name and errors
;    -v2       status line for each file (default)
;    -v3       additional info
;
; DESCRIPTION
;
; VENOM parses files that contain version numbers and returns
; the version number as a string as well as return code.  Useful
; in build scripts and other automated processes.
;
; works on the following types of files
;
; realbase.h
; #define REAL_VERSION 499
; #define REAL_PATCH   0
;
; gcc --version
; 2.95.3
;
; >cl
; Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804
; Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
;
; snd.h
; #define SND_HEADER_LIB_SYNC         27
;
; P2
; (5.2.9.0)
;
; Spit tools
; const char TextVersion[]=""1.53c (Samba) 2007.12.4";
; #define TextVersion ""1.53c (Samba) 2007.12.4"
;
; #define SIMEX_VERSION_MAJOR         7
; #define SIMEX_VERSION_MINOR         9
; #define SIMEX_VERSION_PATCH         0
; #define SIMEX_VERSION ((SIMEX_VERSION_MAJOR << 16)
;         | (SIMEX_VERSION_MINOR << 8) | SIMEX_VERSION_PATCH)
;
; #define EA3_MAJOR_VERSION 3
; #define EA3_MINOR_VERSION 12
; #define EA3_INTERM_VERSION 0
;
; GNU Make version 3.77, by Richard Stallman and Roland McGrath.
; Copyright (C) 1988, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
;   Free Software Foundation, Inc.
;
; Text v1.53c (Vogue) 010530 by FrANK Barchard, EAC
; USAGE: txt -options [files]
;    -pc convert to pc text     -unix convert to unix (SGI)
;
; real-4.30.12-pc.zip
;
; gx.rc
; FILEVERSION 4,56,0,0
;
; Shapes
; 00000: 53485047 50000200 00000001 47323830  SHPGP.......G280
;
; Metrowerks C/C++ Compiler for Embedded PowerPC.
; Copyright (c)1998-2000 Metrowerks, Inc.
; All rights reserved.
; Version 2.3.3 build 159
; Runtime Built: Feb  7 2001 12:08:38
;
; Please enter 'MWCCEPPC.EXE -help' for information a
;
;
; E:\real>mwasmeppc
;
; Metrowerks Assembler for Embedded PowerPC.
; Copyright (c)1998-1999 Metrowerks, Inc.
; All rights reserved.
; Version 2.3.2 build 106
; Runtime Built: Jun  2 2000 15:30:53
;
; Please enter 'MWASME~1.EXE -help' for information a
;
; Tools comment convention
; /* Version Date   SE History:                             */
; /* ------- ----   -- --------                             */
; /*    1.00 940414 FB First gimex prototype module         */
; /*    1.10 940425 FB Reimplimented reentrantly            */
;
; spit tools
; const char TextVersion[]='1.00 (Slubberdegullion) 2003.4.1'
;
; Intel(R) C++ Compiler .. applications, Version 5.0 Build 001120
; Copyright (C) 1985-2000 Intel Corporation.  All rights reserved.
;
; fonts
; XDump v1.66 (Freedom) 011010 by FrANK Barchard, EAC
; 00000: 464e5446 304b0000 c8006000 09000000  FNTF0K..H.`.....
;
; Sony lib versions in libver.h
; #define SCE_LIBRARY_VERSION 0x2500 /* Release Library Version */
;
; Freetype.h style
; #define FREETYPE_MAJOR 2
; #define FREETYPE_MINOR 0
; #define FREETYPE_PATCH 9
;
; GDF .build targetversion=
;
; SAMPLE OUTPUT
;
; > venom realbase.h
; 4.30.11
;
; END ABSTRACT
;
*/
#endif

#if defined(_MSC_VER)
#pragma warning(push,1)
#include <windows.h>
#include <regstr.h>
#include <winbase.h>
#pragma warning(pop)
#endif

#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include "stdgimex.h"
#include "spit.h"
#include "expr.h"
#include "ranger.h"

/****************************************************************/
/*  Spit Setup                                                  */
/****************************************************************/

char OPTprefix[SPITMAXLINE];
char OPTvarname[SPITMAXLINE];
char OPTsuffix[SPITMAXLINE];
char OPTvenom[SPITMAXLINE];
int OPTmatch;
int OPTminor;
char OPTreturn[SPITMAXLINE];

static void VENOM_settitle(char *title)
{
#if defined(_MSC_VER)
    if (OPTverbose)
        SetConsoleTitle(title);
#endif
}

static bool VENOM_isalnum(const char c)
{
    return (c>='A' && c<='Z')
        || (c>='a' && c<='z')
        || (c>='0' && c<='9');
}

static bool VENOM_isexe(const char *source)
{
    if (ggetm(source,2)==0x4d5a)
        return true;
    return false;
}

enum FindLine { FINDLINE_FIRST, FINDLINE_ANY, FINDLINE_DEFINE };

static const char *findstr(const char *source, const char *substr, FindLine findline)
{
    const char *found=0;
    char linebuf[SPITMAXLINE];

    if (findline==FINDLINE_FIRST)
    {
        char *d=linebuf;
        const char *s=source;
        while (*s && *s!=(char)10 && *s!=(char)13)
            *d++ = *s++;
        *d = (char) 0;

        source = (const char *) linebuf;
    }

    while (source)
    {
        found = strstr(source,substr);
        if (found)
        {
            bool founddef=true;
            if (findline==FINDLINE_DEFINE)
            {
                founddef = false;
                const char *def = found;
                while (def != source && (unsigned char)*def>=(unsigned char)' ')
                {
                    if (!strncmp(def,"define",strlen("define")))
                    {
                        founddef = true;
                        break;
                    }
                    --def;
                }
            }

            found += strlen(substr);
            while (*found==' ' || *found=='\t')
                ++found;
            if (*found=='\"')
                ++found;

            if (isdigit(*found) && founddef)
                break;
        }
        source = found;
    }
    return(found);
}

static unsigned short VENOM_SWAP16(unsigned short v)
{
    return v;
//    return (unsigned short)(((v>>8)&255) | ((v&255)<<8));
}

// Structure used to store enumerated languages and code pages.

struct LANGANDCODEPAGE {
  WORD wLanguage;
  WORD wCodePage;
} *lpTranslate;

// ver is buffer to fill out
// verSize is size of ver buffer in bytes
// returns TRUE if successful

bool GetVersionString(const char *exename, char * ver, int verSize)
{
    DWORD dwReserved;
    DWORD dwBufferSize;
    UINT size;
    LPVOID lpBuffer = NULL;
    VOID *lpSubBuffer;
    VOID *lpVerBuffer;
    unsigned int cbTranslate;
    int i;
    bool ok=false;
    bool foundit=false;

    dwBufferSize = GetFileVersionInfoSize((char *)exename,&dwReserved);

    if (!dwBufferSize)
        goto Error;

    lpBuffer=(LPVOID)malloc(dwBufferSize);
    if (!lpBuffer)
        goto Error;
    lpSubBuffer=(LPVOID)malloc(dwBufferSize);
    if (!lpSubBuffer)
        goto Error;
    lpVerBuffer=(LPVOID)malloc(dwBufferSize);
    if (!lpVerBuffer)
        goto Error;


    if (!GetFileVersionInfo((char *)exename,dwReserved,dwBufferSize,lpBuffer))
        goto Error;

    // Try English FileVersion

    {
        wsprintf( (char *)lpSubBuffer,
                      TEXT("\\StringFileInfo\\040904b0\\FileVersion"));


        // Retrieve file description for language and code page "i".
        if (VerQueryValue((char *)lpBuffer,
                              (char *)lpSubBuffer,
                              &lpVerBuffer,
                              &size))
        {
            foundit = true;
        }
    }


    if (!foundit)
    {
        // Read the list of languages and code pages.

        VerQueryValue(lpBuffer,
                      TEXT("\\VarFileInfo\\Translation"),
                      (LPVOID*)&lpTranslate,
                      &cbTranslate);


        // Read the file description for each language and code page.

        for( i=0; i < (int)(cbTranslate/sizeof(struct LANGANDCODEPAGE)); ++i )
        {
            // normally "\\StringFileInfo\\040904b0\\FileVersion"

            wsprintf( (char *)lpSubBuffer,
                      TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"),
                      VENOM_SWAP16(lpTranslate[i].wLanguage),
                      VENOM_SWAP16(lpTranslate[i].wCodePage));

            // Retrieve file description for language and code page "i".
            if (VerQueryValue((char *)lpBuffer,
                              (char *)lpSubBuffer,
                              &lpVerBuffer,
                              &size))
            {
                foundit = true;
                break;
            }
        }
    }

    if (!foundit)
        goto Error;

    if (!size)
        goto Error;

    ok = true;

    strncpy( ver, (char*)lpVerBuffer, min((signed)size,(verSize-1)) );

Error:
    if (lpBuffer)
        free(lpBuffer);

    return(ok);
}

static inline bool ttfistext(char c)
{
    return (unsigned char)c>=(unsigned char)' ' && c<='z';
}

static bool Venom(const char *filename, const char *source, int /*sourcelen*/, int *majorptr, int *minorptr, int *patchptr, int *buildptr)
{

    bool ok=false;
    int args=0;
    int iver = 0;
    const char *found;

    if (!ok)
    {
        // spit style - TextVersion "1.00 (Slubberdegullion) 2003.4.1

        found = findstr(source,"TextVersion \"",FINDLINE_ANY);
        if (found)
        {
            int ccmajor=0, ccminor=0;
            args = sscanf(found,"%d.%d",&ccmajor,&ccminor);
            if (args==2)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = 0;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    if (!ok)
    {
        // spit style2 - TextVersion="1.00 (Slubberdegullion) 2003.4.1

        found = findstr(source,"TextVersion[]=\"",FINDLINE_ANY);
        if (found)
        {
            int ccmajor=0, ccminor=0;
            args = sscanf(found,"%d.%d",&ccmajor,&ccminor);
            if (args==2)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = 0;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    if (!ok)
    {
        // ea3 header style
        found = findstr(source,"_MAJOR_VERSION",FINDLINE_DEFINE);
        if (found)
        {
            iver = 0;
            sscanf(found," %d",&iver);
            *majorptr = iver;
            *minorptr = 0;
            *patchptr = 0;
            *buildptr = 0;

            found = findstr(found,"_MINOR_VERSION",FINDLINE_DEFINE);
            if (found)
            {
                iver = 0;
                sscanf(found," %d",&iver);
                *minorptr = iver;

                found = findstr(found,"_INTERM_VERSION",FINDLINE_DEFINE);
                if (found)
                {
                    iver = 0;
                    sscanf(found," %d",&iver);
                    *patchptr = iver;
                }
            }

            ok = true;
        }
    }


    if (!ok)
    {
        // SIMEX/OMNI style
        found = findstr(source,"_VERSION_MAJOR",FINDLINE_DEFINE);
        if (found)
        {
            iver = 0;
            sscanf(found," %d",&iver);
            *majorptr = iver;
            *minorptr = 0;
            *patchptr = 0;
            *buildptr = 0;

            found = findstr(found,"_VERSION_MINOR",FINDLINE_DEFINE);
            if (found)
            {
                iver = 0;
                sscanf(found," %d",&iver);
                *minorptr = iver;

                found = findstr(found,"_VERSION_PATCH",FINDLINE_DEFINE);
                if (found)
                {
                    iver = 0;
                    sscanf(found," %d",&iver);
                    *patchptr = iver;
                }
            }


            ok = true;
        }
    }

    if (!ok)
    {
        // EAGL/RCMP style
        found = findstr(source,"_MAJOR",FINDLINE_DEFINE);
        if (found)
        {
            iver = 0;
            sscanf(found," %d",&iver);
            *majorptr = iver;
            *minorptr = 0;
            *patchptr = 0;
            *buildptr = 0;

            found = findstr(found,"_MINOR",FINDLINE_DEFINE);
            if (found)
            {
                iver = 0;
                sscanf(found," %d",&iver);
                *minorptr = iver;

                found = findstr(found,"_PATCH",FINDLINE_DEFINE);
                if (found)
                {
                    iver = 0;
                    sscanf(found," %d",&iver);
                    *patchptr = iver;
                }
            }

            ok = true;
        }
    }

    // Sony Library style SCE_LIBRARY_VERSION 0x2500

    if (!ok)
    {
        found = findstr(source,"SCE_LIBRARY_VERSION",FINDLINE_DEFINE);
        if (found)
        {
            int ccmajor=0;
            args = sscanf(found," 0x%x",&ccmajor);
            if (args==1)
            {
                *majorptr = ccmajor>>12;
                *minorptr = ((ccmajor>>8)&15)*10 + ((ccmajor>>4)&15);
                *patchptr = ccmajor&15;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    if (!ok)
    {
        // ZLIB style "1.24"
        // #define ZLIB_VERSION "1.1.4"
        found = findstr(source,"_VERSION",FINDLINE_DEFINE);
        if (found)
        {
            int ccmajor=0, ccminor=0, ccpatch=0;
            while ((unsigned char)*found>=(unsigned char)' ' && !isdigit(*found))
                ++found;
            args = sscanf(found,"%d.%d.%d",&ccmajor,&ccminor, &ccpatch);
            if (args>=2)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    if (!ok)
    {
        // REAL style 512
        found = findstr(source,"_VERSION",FINDLINE_DEFINE);
        if (found)
        {
            iver = 0;
            sscanf(found," %d",&iver);
            *majorptr = iver;
            *minorptr = 0;
            *patchptr = 0;
            *buildptr = 0;
            if (iver>100)
            {
                *majorptr = iver/100;
                *minorptr = iver%100;
                *patchptr = 0;
                if (iver>10000)
                {
                    *majorptr = iver/10000;
                    *minorptr = (iver/100)%100;
                    *patchptr = iver%100;
                }
            }
            found = findstr(found,"_PATCH",FINDLINE_DEFINE);
            if (found)
            {
                iver = 0;
                sscanf(found," %d",&iver);
                *patchptr = iver;
            }
            ok = true;
        }
    }


    if (!ok)
    {
        // XTL style 5120
        found = findstr(source,"_VER",FINDLINE_DEFINE);
        if (found)
        {
            iver = 0;
            sscanf(found," %d",&iver);
            *majorptr = iver;
            *minorptr = 0;
            *patchptr = 0;
            *buildptr = 0;
            ok = true;
        }
    }

    if (!ok)
    {
        // EAGL style 2
        found = findstr(source,"_MAJOR",FINDLINE_DEFINE);
        if (found)
        {
            iver = 0;
            sscanf(found," %d",&iver);
            *majorptr = iver;
            *minorptr = 0;
            *patchptr = 0;
            *buildptr = 0;

            found = findstr(found,"_MINOR",FINDLINE_DEFINE);
            if (found)
            {
                iver = 0;
                sscanf(found," %d",&iver);
                *minorptr = iver;

                found = findstr(found,"_PATCH",FINDLINE_DEFINE);
                if (found)
                {
                    iver = 0;
                    sscanf(found," %d",&iver);
                    *patchptr = iver;
                }
            }

            ok = true;
        }
    }



    // snd style
    if (!ok)
    {
        found = findstr(source,"_HEADER_LIB_SYNC",FINDLINE_DEFINE);
        if (found)
        {
            iver = 0;
            sscanf(found," %d",&iver);
            *majorptr = 0;
            *minorptr = iver;
            *patchptr = 0;
            *buildptr = 0;
            ok = true;
        }
    }


    // make file containing /version:1.23 link directive

    if (!ok)
    {
        found = findstr(source,"/version:",FINDLINE_ANY);
        if (found)
        {
            int ccmajor=0, ccminor=0;
            args = sscanf(found,"%d.%d",&ccmajor,&ccminor);
            if (args==2)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = 0;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // resource .rc style  FILEVERSION 4,56,0,0

    if (!ok)
    {
        found = findstr(source,"FILEVERSION",FINDLINE_ANY);
        if (found)
        {
            int ccmajor=0, ccminor=0, ccpatch=0,ccbuild=0;
            args = sscanf(found,"%d,%d,%d,%d",&ccmajor,&ccminor, &ccpatch, &ccbuild);
            if (args==4)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = ccbuild;
                ok = true;
            }
        }
    }


    // Master.build
    // <package name="PlayStation2SN" targetversion="2.7.0"/>

    if (!ok)
    {
        found = findstr(source,"targetversion=\"",FINDLINE_ANY);
        if (found)
        {
            int ccmajor=0, ccminor=0, ccpatch=0;
            args = sscanf(found,"%d.%d.%d",&ccmajor,&ccminor, &ccpatch);
            if (args==3)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = 0;
                ok = true;
            }
        }
    }


    // chunks versions (shapes/fonts/bigfiles)
    // 00000: 53485047 50000200 00000001 47323830  SHPGP.......G280
    if (!ok)
    {
        bool ischunk=true;
        bool foundzero=false;
        int i;
        for (i=0; i<4; ++i)
        {
            if (!VENOM_isalnum(source[i]))
                ischunk = false;
            if (!VENOM_isalnum(source[i+12]))
                ischunk = false;
        }
        for (i=4; i<12; ++i)
        {
            if (!source[i])
                foundzero = true;
        }

        if (ischunk && foundzero)
        {
            char buf[8];
            memset(buf,0,sizeof(buf));
            if (isalpha(source[12]))
                memcpy(buf,source+13,3);
            else
                memcpy(buf,source+12,4);

            iver = 0;
            sscanf(buf,"%d",&iver);
            *majorptr = iver/100;
            *minorptr = iver%100;
            *patchptr = 0;
            *buildptr = 0;
            ok = true;
        }
    }

    // chunks versions (fonts)
    // 00000: 464e5446 304b0000 c8006000 09000000  FNTF0K..H

    if (!ok)
    {
        if ((ggetm(source,4)&0xffffff00)==GMAKEID('F','N','T',0)
         || (ggetm(source,4)&0xffffff00)==GMAKEID('F','n','t',0))
        {
            if (source[3]=='G' || source[3]=='M')
                iver = ggetm(source+8,2);
            else
                iver = ggeti(source+8,2);

            if (iver>=100 && iver<=999)
            {
                *majorptr = iver/100;
                *minorptr = iver%100;
                *patchptr = 0;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // Big files ('L218' follows table of contents)
    // 00000: 42494746 85300000 00000001 00000027  BIGF.0.........'
    // 00010: 00000028 0000305d 73706974 2e68004c  ...(..0]spit.h.L
    // 00020: 32313800 00000000 2f2a2043 6f707972  218...../* Copyr


    if (!ok)
    {
        int headersize=0;
        if ((ggetm(source+0,4)&0xffffff00)==GMAKEID('B','I','G',0)
         || (ggetm(source+0,4)&0xffffff00)==GMAKEID('S','h','p',0))
        {
            headersize = ggetm(source+12,4);
        }
        else if (ggetm(source+0,2)==0xc0fb)
        {
            headersize = ggetm(source+2,2)+4;
        }

        if (headersize)
        {
            const char *venomptr = source+headersize-8;
            bool ischunk = true;

            for (int i=0; i<4; ++i)
            {
                if (!VENOM_isalnum(venomptr[i]))
                    ischunk = false;
            }

            if (ischunk)
            {
                char buf[8];
                memset(buf,0,sizeof(buf));
                if (isalpha(venomptr[0]))
                    memcpy(buf,venomptr+1,3);
                else
                    memcpy(buf,venomptr,4);

                iver = 0;
                sscanf(buf,"%d",&iver);
                *majorptr = iver/100;
                *minorptr = iver%100;
                *patchptr = 0;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // ttf - ie arial.ttf
    //00000: 00010000 00170100 00040070 44534947  ...........pDSIG
    //00010: 80b1e5c2 000412dc 00001410 47444546  .1eB...\....GDEF
    //00020: 51c75137 000426ec 00000098 47535542  QGQ7..&l....GSUB

    if (!ok)
    {
        int ccmajor = ggetm(source+0,2);
        int ccminor = ggetm(source+2,2);
        if (ccmajor>=0x0001 && ccmajor<=0x0008
         && ccminor>=0x0000 && ccminor<=0x0040
         && ttfistext(source[12])
         && ttfistext(source[13])
         && ttfistext(source[14])
         && ttfistext(source[15]))
        {
            *majorptr = ccmajor;
            *minorptr = ccminor;
            *patchptr = 0;
            *buildptr = 0;
            ok = true;
        }
    }

    // visual c style
    if (!ok)
    {
        found = findstr(source,"Version",FINDLINE_FIRST);
        if (found)
        {
            int ccmajor=0, ccminor=0, ccpatch=0;
            args = sscanf(found,"%d.%d.%d",&ccmajor,&ccminor, &ccpatch);
            if (args==3)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // metrowerks style - Version 2.3.3 build 159
    if (!ok)
    {
        found = findstr(source,"Version",FINDLINE_ANY);
        if (found)
        {
            int ccmajor=0, ccminor=0, ccpatch=0, ccbuild=0;
            args = sscanf(found,"%d.%d.%d build %d",&ccmajor,&ccminor,&ccpatch,&ccbuild);
            if (args==4)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = ccbuild;
                ok = true;
            }
        }
    }


    // make style version

    if (!ok)
    {
        found = findstr(source,"version",FINDLINE_FIRST);
        if (found)
        {
            int ccmajor=0, ccminor=0;
            args = sscanf(found,"%d.%d",&ccmajor,&ccminor);
            if (args==2)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = 0;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // SN Versions.txt format.  ie Version 1.53

    if (!ok)
    {
        found = source;
        for (int trycount=0; found && !ok && trycount<10; ++trycount)
        {
            found = findstr(found,"Version ",FINDLINE_ANY);

            if (found)
            {
                int ccmajor=0, ccminor=0;
                args = sscanf(found,"%d.%d",&ccmajor,&ccminor);
                if (args==2)
                {
                    *majorptr = ccmajor;
                    *minorptr = ccminor;
                    *patchptr = 0;
                    *buildptr = 0;
                    ok = true;
                }
            }
        }
    }

    // spit tool style v1.00
    if (!ok)
    {
        found = findstr(source,"v",FINDLINE_FIRST);
        if (found)
        {
            int ccmajor=0, ccminor=0, ccpatch=0;
            args = sscanf(found,"%d.%d",&ccmajor,&ccminor);
            if (args==2)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // eactools file naming style - real-4.30.12-pc.zip
    if (!ok)
    {
        found = findstr(source,"-",FINDLINE_FIRST);
        if (found)
        {
            int ccmajor=0, ccminor=0, ccpatch=0;
            args = sscanf(found,"%d.%d.%d",&ccmajor,&ccminor,&ccpatch);
            if (args==3)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // dll/exe

    {
        char CurrentFolderName[SPITMAXLINE];
        sprintf(CurrentFolderName,"./%s",filename);
        char VersionString[SPITMAXLINE];

        bool founddll = founddll = GetVersionString(CurrentFolderName, VersionString, sizeof(VersionString)-1 );
        if (!founddll)
        {
            founddll = GetVersionString(filename, VersionString, sizeof(VersionString)-1 );
        }

        if (founddll)
        {
            int ccmajor=0, ccminor=0, ccpatch=0, ccbuild=0;
            args = sscanf(VersionString,"%d.%d.%d.%d",&ccmajor,&ccminor,&ccpatch,&ccbuild);

            if (args<=1)
                args = sscanf(VersionString,"%d,%d,%d,%d",&ccmajor,&ccminor,&ccpatch,&ccbuild);

            if (args==4)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = ccbuild;
                ok = true;
            }
            else if (args==3)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = ccpatch;
                *buildptr = 0;
                ok = true;
            }
            else if (args==2)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = 0;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // dll/exe - /version:3.58 imbedded version in exe/dll header
    // tplgimex.8bi
    // 0x3c points to 'PE' header.  PE+0x2c has version
    // 00000: 4d5a9000 03000000 04000000 ffff0000  MZ..............
    // 00010: b8000000 00000000 40000000 00000000  8.......@.......
    // 00020: 00000000 00000000 00000000 00000000  ................
    // 00030: 00000000 00000000 00000000 f0000000  ............p...
    // 00040: 0e1fba0e 00b409cd 21b8014c cd215468  ..:..4.M!8.LM!Th
    // 00050: 69732070 726f6772 616d2063 616e6e6f  is program canno
    // 00060: 74206265 2072756e 20696e20 444f5320  t be run in DOS
    // 00070: 6d6f6465 2e0d0d0a 24000000 00000000  mode....$.......
    // 00080: 710debf4 356c85a7 356c85a7 356c85a7  q.kt5l.'5l.'5l.'
    // 00090: 4e7089a7 326c85a7 b6708ba7 376c85a7  Np.'2l.'6p.'7l.'
    // 000a0: 5a7381a7 376c85a7 356c84a7 706c85a7  Zs.'7l.'5l.'pl.'
    // 000b0: 577396a7 326c85a7 614fb5a7 336c85a7  Ws.'2l.'aO5'3l.'
    // 000c0: 614fb4a7 316c85a7 f26a83a7 346c85a7  aO4'1l.'rj.'4l.'
    // 000d0: ca4c81a7 346c85a7 52696368 356c85a7  JL.'4l.'Rich5l.'
    // 000e0: 00000000 00000000 00000000 00000000  ................
    // 000f0: 50450000 4c010500 afed3a3d 00000000  PE..L.../m:=....
    // 00100: 00000000 e0000e21 0b010600 00700000  ....`..!.....p..
    // 00110: 00600000 00000000 10230000 00100000  .`.......#......
    // 00120: 00800000 00000010 00100000 00100000  ................

    if (!ok)
    {
        int peoffset = ggeti(source+0x3c,4);

        if (VENOM_isexe(source)
         && peoffset>0x40 && peoffset<0x200)
        {
            int ccmajor = ggeti(source+peoffset+0x44,2);
            int ccminor = ggeti(source+peoffset+0x46,2);

            if (ggetm(source+peoffset,2)==0x5045 // PE header
             && (ccmajor || ccminor)
             && ccmajor>=0 && ccmajor<=100
             && ccminor>=0 && ccminor<=100)
            {
                *majorptr = ccmajor;
                *minorptr = ccminor;
                *patchptr = 0;
                *buildptr = 0;
                ok = true;
            }
        }
    }

    // gcc style and devlog style
    // any line with white spaces and a version in the form 12.34.56
    // gcc --version and devlog.txt contain these
    // could be error prone... do this last

    if (!ok)
    {
        found = source;
        while (*found)
        {
            while (*found==' ' || *found=='\t')
                ++found;
            if (isdigit(*found))
            {
                int ccmajor=0, ccminor=0, ccpatch=0;
                args = sscanf(found,"%d.%d.%d",&ccmajor,&ccminor, &ccpatch);
                if (args==3
                && ccmajor>=1 && ccmajor<=9999
                && ccminor>=0 && ccminor<=9999
                && ccpatch>=0 && ccpatch<=999999)
                {
                    *majorptr = ccmajor;
                    *minorptr = ccminor;
                    *patchptr = ccpatch;
                    *buildptr = 0;
                    ok = true;
                    break;
                }
            }
            while (*found && *found!=(char)10 && *found!=(char)13)
                ++found;
            if (*found==(char)13)
                ++found;
            if (*found==(char)10)
                ++found;
        }
    }

    return(ok);
}

void SPIT_reset(void)
{
    OPTbanner = 0;
    OPTverbose = 2;
    strcpy(OPTvarname,"");
    strcpy(OPTprefix,"");
    strcpy(OPTsuffix,"");
    strcpy(OPTvenom,"%s");
    OPTmatch = 0;
    OPTminor = 0;
    strcpy(OPTreturn,"");

    return;
}

char *optionhelp[]=
{
    "    @file     response file, containing a list of options and files\n",
    "    =file     write to a file\n",
    "    -match    require versions of multiple files to match\n"
    "    -match0   do not require versions of multiple files to match (default)\n",
    "    -return<expr> return expression (default major*100+minor)\n"
    "              variables available: major, minor, patch, build, size\n",
    "    -nologo   disable banner\n",
    "    -v0       no console output\n",
    "    -v1       prints source file name and errors\n",
    "    -v2       status line for each file (default)\n",
    "    -v3       additional info\n",
    "\n",
    0,
};


void SPIT_usage(const char *prgname, const char *envname, const char *urlname, int utype)
{
    char **hlp;

    printf("VENOM v%s (C) 2002 FrANK G. Barchard, EAC\n",TextVersion);

    if (utype)
        printf("USAGE: %s <inputfile>\n", prgname);

    switch (utype)
    {
        case 0:
        case 1:
            break;

        case 2:
            puts("  -match require files to match");
          printf("type '%s -h' for more help\n", prgname);
          printf("type '%s -help' for web page (%s)\n", prgname, urlname);
            break;

        case 3:
            puts("");
          printf("VERSION: %s v%s, Spit v%.2f\n",SPIT_MACHINE,TextVersion,(float)SPIT_VERSION/100.0f);
            puts("SUMMARY: VENOM prints the user name and current time");
          printf("         SPIT environment variable is %s\n",envname);
            puts("");

            hlp = optionhelp;
            while (*hlp)
            {
                printf("%s",*hlp);
                ++hlp;
            }
            break;

         case 4:
            puts("");
            {
                hlp = optionhelp;
                while (*hlp)
                {
                    if (SPIT_strstr(*hlp,envname))
                        printf("%s",*hlp);
                    ++hlp;
                }
                puts("");
            }
            break;


        default:
            break;
    }
}


char *SPIT_opt(const char *optptr)
{
    char *d=NULL;
    switch (tolower(*optptr))
    {
        case 'a':
            d = SPIT_getstropt( OPTvenom,optptr+1, "venom name or formatter", "", SPITYES, SPITMAXLINE);
            break;

        case 'n':
            d = SPIT_getstropt( OPTvarname,optptr+1, "variable name", "", SPITYES, SPITMAXLINE);
            break;

        case 'p':
            d = SPIT_getstropt( OPTprefix,optptr+1, "prefix string", "", SPITYES, SPITMAXLINE);
            break;

        case 'r':
            if (!strnicmp(optptr,"return",6))
            {
                d = SPIT_getfield( OPTreturn,optptr+6,1,0);
            }
            break;

        case 's':
            d = SPIT_getstropt( OPTsuffix,optptr+1, "suffix string", "", SPITYES, SPITMAXLINE);
            break;

        case 'm':
            if (!strnicmp(optptr,"match",5))
                d = SPIT_getopt( &OPTmatch,optptr+5, "require matching versions", 0, 1);
            else if (!strnicmp(optptr,"minor",5))
                d = SPIT_getopt( &OPTminor,optptr+5, "display major.minor", 0, 1);

            break;

        default: ;
    }
    return(d);
}

int main(int argc,char **argv)
{
    int i;
    int more;
    int returncode=0;
    int firstcode=0;
    SPITMEM infile;
    double totaltime;

    SPIT_init(SPIT_reset, SPIT_usage, SPIT_opt, SPIT_history);
    totaltime = SPIT_gettime();
    do
    {
        more = SPIT_parse(argc, argv, TOOL_NAME, TOOL_VERSION);

        for (i=0; i<SPITargnum; ++i)
        {
            char statusBuffer[512];
            sprintf(statusBuffer,"%s - venom",SPITsargv[i]);
            VENOM_settitle(statusBuffer);

            if (SPIT_loadfilev(&infile, SPITsargv[i], 0))
            {
                int major, minor, patch, build;
                int ok;

                ok = Venom(SPITsargv[i], infile.ptr, infile.len, &major, &minor, &patch, &build);

                SPIT_free(&infile);

                if (OPTmatch)
                {
                    if (i==0)
                        firstcode = major*100+minor;
                    else if (firstcode != major*100+minor)
                        ++returncode;
                }
                else
                {
                    returncode = major*100+minor;
                }
                if (OPTreturn[0])
                {
                    EXPR *expr = EXPR_open();
                    RANGER *r = RANGER_open(expr,OPTreturn,0.0,1000000.0);  // min/max mostly ignored

                    EXPR_set(expr,"major",major);
                    EXPR_set(expr,"minor",minor);
                    EXPR_set(expr,"patch",patch);
                    EXPR_set(expr,"build",build);
                    EXPR_set(expr,"size",infile.len);

                    returncode = (int) RANGER_closest(r, (double) returncode, RANGER_ROUND_CLOSEST);

                    EXPR_close(expr);
                    RANGER_close(r);
                }

                if (OPTverbose)
                {
                    if (ok)
                    {
                        if (OPTverbose>=3)
                            printf("%s ",SPITsargv[i]);

                        if (OPTminor)
                        {
                            if (OPTverbose>=5)
                                printf("Major %d Minor %d\n", major, minor);
                            else if (OPTverbose>=2)
                                printf("%d.%d\n", major, minor);
                        }
                        else if (build || OPTverbose>=4)
                        {
                            if (OPTverbose>=5)
                                printf("Major %d Minor %d Patch %d Build %d\n", major, minor, patch, build);
                            else if (OPTverbose>=2)
                                printf("%d.%d.%d.%d\n", major, minor, patch,build);                     }
                        else
                        {
                            if (OPTverbose>=5)
                                printf("Major %d Minor %d Patch %d\n", major, minor, patch);
                            else if (OPTverbose>=2)
                                printf("%d.%d.%d\n", major, minor, patch);
                        }
                    }
                    else if (OPTverbose>3)
                        printf("%s Version not found\n",SPITsargv[i]);
                    SPIT_flush();
                }
            }
        }
    } while (more>0);

    SPIT_restore();

    if (OPTmatch)
    {
        if (OPTverbose>=3)
        {
            if (returncode>1)
                printf("VENOM - ERROR %d VERSIONS DO NOT MATCH FIRST VERSION %d\n",returncode,firstcode);
            else
                printf("VENOM - ERROR VERSION DID NOT MATCH FIRST VERSION %d\n",firstcode);
        }
    }

    if (galloccount)
        printf("galloccount = %i\n", galloccount);

    totaltime = SPIT_gettime()-totaltime;

    if (OPTtime)
    {
        if (totaltime>=60000.0)
            printf("TIME " TOOL_NAME " totaltime %.2f ms (%.2f seconds) (%.2f minutes)\n",totaltime,totaltime/1000.0,totaltime/60000.0);
        else if (totaltime>=1000.0)
            printf("TIME " TOOL_NAME " totaltime %.2f ms (%.2f seconds)\n",totaltime,totaltime/1000.0);
        else
            printf("TIME " TOOL_NAME " totaltime %.2f ms\n",totaltime);
    }

    return(returncode);
}

