/****************************************************************************/
/*                                                                          */
/*  rcx.C - AFX symbol info writer                                          */
/*                                                                          */
/*    Windows 3.5 Resource Compiler                                         */
/*                                                                          */
/****************************************************************************/

#include "rc.h"

/////////////////////////////////////////////////////////////////////////////

// Symbol information
static PFILE    fhFileMap;
static LONG     lFileMap;

static PFILE    fhResMap;
static LONG     lResMap;

static PFILE    fhRefMap;
static LONG     lRefMap;

static PFILE    fhSymList;
static LONG     lSymList;

static LONG     HdrOffset;

static CHAR     szEndOfResource[2] = {'$', '\000'};

static CHAR     szSymList[_MAX_PATH];
static CHAR     szFileMap[_MAX_PATH];
static CHAR     szRefMap[_MAX_PATH];
static CHAR     szResMap[_MAX_PATH];

static WCHAR    szName[] = L"HWB";


#define OPEN_FLAGS (_O_TRUNC | _O_BINARY | _O_CREAT | _O_RDWR)
#define PROT_FLAGS (S_IWRITE | S_IWRITE)

void
wtoa(
    WORD value,
    char* string,
    int radix
    )
{
    if (value == (WORD)-1)
        _itoa(-1, string, radix);
    else
        _itoa(value, string, radix);
}

int
ConvertAndWrite(
    PFILE fp,
    PWCHAR pwch
    )
{
    int  n;
    char szMultiByte[_MAX_PATH];        // assumes _MAX_PATH >= MAX_SYMBOL

    n = wcslen(pwch) + 1;
    n = WideCharToMultiByte(uiCodePage, 0,
                pwch, n,
                szMultiByte, MAX_PATH,
                NULL, NULL);
    return MyWrite(fp, (PVOID)szMultiByte, n);
}

VOID
WriteResHdr (
    FILE *fh,
    LONG size,
    WORD id
    )
{
    LONG     val;

    /* add data size and header size */
    MyWrite(fh, (PVOID)&size, sizeof(ULONG)); // will backpatch
    MyWrite(fh, (PVOID)&HdrOffset, sizeof(ULONG));

    /* add type and name */
    MyWrite(fh, (PVOID)szName, sizeof(szName));
    val = 0xFFFF;
    MyWrite(fh, (PVOID)&val, sizeof(WORD));
    MyWrite(fh, (PVOID)&id, sizeof(WORD));

    MyAlign(fh);

    /* add data struct version, flags, language, resource data version
    /*  and characteristics */
    val = 0;
    MyWrite(fh, (PVOID)&val, sizeof(ULONG));
    val = 0x0030;
    MyWrite(fh, (PVOID)&val, sizeof(WORD));
    MyWrite(fh, (PVOID)&language, sizeof(WORD));
    val = 2;
    MyWrite(fh, (PVOID)&val, sizeof(ULONG));
    MyWrite(fh, (PVOID)&characteristics, sizeof(ULONG));
}

BOOL
InitSymbolInfo(
    void
    )
{
    PCHAR   szTmp;

    if (!fAFXSymbols)
        return(TRUE);

    if ((szTmp = _tempnam(NULL, "RCX1")) != NULL) {
        strcpy(szSymList, szTmp);
        free(szTmp);
    } else {
        strcpy(szSymList, tmpnam(NULL));
    }

    if ((szTmp = _tempnam(NULL, "RCX2")) != NULL) {
        strcpy(szFileMap, szTmp);
        free(szTmp);
    } else {
        strcpy(szFileMap, tmpnam(NULL));
    }

    if ((szTmp = _tempnam(NULL, "RCX3")) != NULL) {
        strcpy(szRefMap, szTmp);
        free(szTmp);
    } else {
        strcpy(szRefMap, tmpnam(NULL));
    }

    if ((szTmp = _tempnam(NULL, "RCX4")) != NULL) {
        strcpy(szResMap, szTmp);
        free(szTmp);
    } else {
        strcpy(szResMap, tmpnam(NULL));
    }

    if (!(fhFileMap = fopen(szFileMap, "w+b")) ||
        !(fhSymList = fopen(szSymList, "w+b")) ||
        !(fhRefMap  = fopen(szRefMap,  "w+b")) ||
        !(fhResMap  = fopen(szResMap,  "w+b")))
        return FALSE;

    /* calculate header size */
    HdrOffset = sizeof(szName);
    HdrOffset += 2 * sizeof(WORD);
    if (HdrOffset % 4)
        HdrOffset += sizeof(WORD);        // could only be off by 2
    HdrOffset += sizeof(RESADDITIONAL);

    WriteResHdr(fhSymList, lSymList, 200);
    WriteResHdr(fhFileMap, lFileMap, 201);
    WriteResHdr(fhRefMap, lRefMap, 202);
    WriteResHdr(fhResMap, lResMap, 2);

    return TRUE;
}

BOOL
TermSymbolInfo(
    PFILE fhResFile
    )
{
    long        lStart;
    PTYPEINFO   pType;
    RESINFO     r;

    if (!fAFXSymbols)
        return(TRUE);

    if (fhResFile == NULL_FILE)
        goto termCloseOnly;

    WriteSymbolDef(L"", L"", L"", 0, (char)0);
    MySeek(fhSymList, 0L, SEEK_SET);
    MyWrite(fhSymList, (PVOID)&lSymList, sizeof(lSymList));

    MySeek(fhFileMap, 0L, SEEK_SET);
    MyWrite(fhFileMap, (PVOID)&lFileMap, sizeof(lFileMap));

    WriteResInfo(NULL, NULL, FALSE);
    MySeek(fhRefMap, 0L, SEEK_SET);
    MyWrite(fhRefMap, (PVOID)&lRefMap, sizeof(lRefMap));

    // now append these to .res
    pType = AddResType(L"HWB", 0);
    r.flags = 0x0030;
    r.name = NULL;
    r.next = NULL;
    r.language = language;
    r.version = version;
    r.characteristics = characteristics;

    MySeek(fhSymList, 0L, SEEK_SET);
    MyAlign(fhResFile);
    r.BinOffset = MySeek(fhResFile, 0L, SEEK_END) + HdrOffset;
    r.size = lSymList;
    r.nameord = 200;
    WriteResInfo(&r, pType, TRUE);
    MyCopyAll(fhSymList, fhResFile);

    MySeek(fhFileMap, 0L, SEEK_SET);
    MyAlign(fhResFile);
    r.BinOffset = MySeek(fhResFile, 0L, SEEK_END) + HdrOffset;
    r.size = lFileMap;
    r.nameord = 201;
    WriteResInfo(&r, pType, TRUE);
    MyCopyAll(fhFileMap, fhResFile);

    MySeek(fhRefMap, 0L, SEEK_SET);
    MyAlign(fhResFile);
    r.BinOffset = MySeek(fhResFile, 0L, SEEK_END) + HdrOffset;
    r.size = lRefMap;
    r.nameord = 202;
    WriteResInfo(&r, pType, TRUE);
    MyCopyAll(fhRefMap, fhResFile);

    MyAlign(fhResFile);
    lStart = MySeek(fhResFile, 0L, SEEK_CUR);
    MySeek(fhResMap, 0L, SEEK_SET);
    MyWrite(fhResMap, (PVOID)&lResMap, sizeof(lResMap));
    MySeek(fhResMap, 0L, SEEK_SET);
    MyCopyAll(fhResMap, fhResFile);

    // patch the HWB:1 resource with HWB:2's starting point
    MySeek(fhResFile, lOffIndex, SEEK_SET);
    MyWrite(fhResFile, (PVOID)&lStart, sizeof(lStart));

    MySeek(fhResFile, 0L, SEEK_END);

termCloseOnly:;

    if (fhFileMap) {
        fclose(fhFileMap);
        remove(szFileMap);
    }

    if (fhRefMap) {
        fclose(fhRefMap);
        remove(szRefMap);
    }

    if (fhSymList) {
        fclose(fhSymList);
        remove(szSymList);
    }

    if (fhResMap) {
        fclose(fhResMap);
        remove(szResMap);
    }

    return TRUE;
}


void
WriteSymbolUse(
    PSYMINFO pSym
    )
{
    if (!fAFXSymbols)
        return;

    if (pSym == NULL) {
        WORD nID = (WORD)-1;

        lRefMap += MyWrite(fhRefMap, (PVOID)&szEndOfResource, sizeof(szEndOfResource));
        lRefMap += MyWrite(fhRefMap, (PVOID)&nID, sizeof(nID));
    } else {
        lRefMap += ConvertAndWrite(fhRefMap, pSym->name);
        lRefMap += MyWrite(fhRefMap, (PVOID)&pSym->nID, sizeof(pSym->nID));
    }
}


void
WriteSymbolDef(
    PWCHAR name,
    PWCHAR value,
    PWCHAR file,
    WORD line,
    char flags
    )
{
    if (!fAFXSymbols)
        return;

    if (name[0] == L'$' && value[0] != L'\0') {
        RESINFO     res;
        TYPEINFO    typ;

        res.nameord  = (USHORT) -1;
        res.language = language;
        typ.typeord  = (USHORT) -1;
        WriteFileInfo(&res, &typ, value);
        return;
    }

    lSymList += ConvertAndWrite(fhSymList, name);
    lSymList += ConvertAndWrite(fhSymList, value);

    lSymList += MyWrite(fhSymList, (PVOID)&line, sizeof(line));
    lSymList += MyWrite(fhSymList, (PVOID)&flags, sizeof(flags));
}


void
WriteFileInfo(
    PRESINFO pRes,
    PTYPEINFO pType,
    PWCHAR szFileName
    )
{
    WORD n1 = 0xFFFF;

    if (!fAFXSymbols)
        return;

    if (pType->typeord == 0) {
        lFileMap += MyWrite(fhFileMap, (PVOID)pType->type,
                            (wcslen(pType->type) + 1) * sizeof(WCHAR));
    } else {
        WORD n2 = pType->typeord;

        if (n2 == (WORD)RT_MENUEX)
            n2 = (WORD)RT_MENU;
        else if (n2 == (WORD)RT_DIALOGEX)
            n2 = (WORD)RT_DIALOG;
        lFileMap += MyWrite(fhFileMap, (PVOID)&n1, sizeof(WORD));
        lFileMap += MyWrite(fhFileMap, (PVOID)&n2, sizeof(WORD));
    }

    if (pRes->nameord == 0) {
        lFileMap += MyWrite(fhFileMap, (PVOID)pRes->name,
                            (wcslen(pRes->name) + 1) * sizeof(WCHAR));
    } else {
        lFileMap += MyWrite(fhFileMap, (PVOID)&n1, sizeof(WORD));
        lFileMap += MyWrite(fhFileMap, (PVOID)&pRes->nameord, sizeof(WORD));
    }

    lFileMap += MyWrite(fhFileMap, (PVOID)&pRes->language, sizeof(WORD));
    lFileMap += MyWrite(fhFileMap, (PVOID)szFileName,
                        (wcslen(szFileName) + 1) * sizeof(WCHAR));
}


void
WriteResInfo(
    PRESINFO pRes,
    PTYPEINFO pType,
    BOOL bWriteMapEntry
    )
{
    if (!fAFXSymbols)
        return;

    if (pRes == NULL) {
        WORD nID = (WORD)-1;

        //assert(bWriteMapEntry == FALSE);
        lRefMap += MyWrite(fhRefMap, (PVOID)&szEndOfResource, sizeof(szEndOfResource));
        lRefMap += MyWrite(fhRefMap, (PVOID)&nID, sizeof(nID));

        return;
    }

    if (bWriteMapEntry) {
        WORD n1 = 0xFFFF;
        ULONG t0 = 0;

        /* add data size and data offset */
        lResMap += MyWrite(fhResMap, (PVOID)&pRes->size, sizeof(ULONG));
        lResMap += MyWrite(fhResMap, (PVOID)&pRes->BinOffset, sizeof(ULONG));

        /* Is this an ordinal type? */
        if (pType->typeord) {
            WORD n2 = pType->typeord;

            if (n2 == (WORD)RT_MENUEX)
                n2 = (WORD)RT_MENU;
            else if (n2 == (WORD)RT_DIALOGEX)
                n2 = (WORD)RT_DIALOG;
            lResMap += MyWrite(fhResMap, (PVOID)&n1, sizeof(WORD));
            lResMap += MyWrite(fhResMap, (PVOID)&n2, sizeof(WORD));
        } else {
            lResMap += MyWrite(fhResMap, (PVOID)pType->type,
                               (wcslen(pType->type) + 1) * sizeof(WCHAR));
        }

        if (pRes->nameord) {
            lResMap += MyWrite(fhResMap, (PVOID)&n1, sizeof(WORD));
            lResMap += MyWrite(fhResMap, (PVOID)&pRes->nameord, sizeof(WORD));
        } else {
            lResMap += MyWrite(fhResMap, (PVOID)pRes->name,
                               (wcslen(pRes->name) + 1) * sizeof(WCHAR));
        }

        lResMap += MyAlign(fhResMap);

        /* add data struct version, flags, language, resource data version
        /*  and characteristics */
        lResMap += MyWrite(fhResMap, (PVOID)&t0, sizeof(ULONG));
        lResMap += MyWrite(fhResMap, (PVOID)&pRes->flags, sizeof(WORD));
        lResMap += MyWrite(fhResMap, (PVOID)&pRes->language, sizeof(WORD));
        lResMap += MyWrite(fhResMap, (PVOID)&pRes->version, sizeof(ULONG));
        lResMap += MyWrite(fhResMap, (PVOID)&pRes->characteristics, sizeof(ULONG));

        return;
    }

    if (pType->typeord == 0) {
        lRefMap += ConvertAndWrite(fhRefMap, pType->type);
    } else {
        char szID[33];
        WORD n2 = pType->typeord;

        if (n2 == (WORD)RT_MENUEX)
            n2 = (WORD)RT_MENU;
        else if (n2 == (WORD)RT_DIALOGEX)
            n2 = (WORD)RT_DIALOG;

        wtoa(n2, szID, 10);
        lRefMap += MyWrite(fhRefMap, (PVOID)szID, strlen(szID)+1);
    }

    if (pRes->nameord == 0) {
        lRefMap += ConvertAndWrite(fhRefMap, pRes->name);
    } else {
        char szID[33];

        wtoa(pRes->nameord, szID, 10);
        lRefMap += MyWrite(fhRefMap, (PVOID)szID, strlen(szID)+1);
    }

    lRefMap += ConvertAndWrite(fhRefMap, pRes->sym.name);
    lRefMap += ConvertAndWrite(fhRefMap, pRes->sym.file);
    lRefMap += MyWrite(fhRefMap,(PVOID)&pRes->sym.line,sizeof(pRes->sym.line));
}

/*---------------------------------------------------------------------------*/
/*                                                                           */
/*      GetSymbolDef() - get a symbol def record and write out info          */
/*                                                                           */
/*---------------------------------------------------------------------------*/
void
GetSymbolDef(
    int fReportError,
    WCHAR curChar
    )
{
    SYMINFO sym;
    WCHAR   szDefn[_MAX_PATH];
    WCHAR   szLine[16];
    PWCHAR  p;
    CHAR    flags = 0;
    WCHAR   currentChar = curChar;

    if (!fAFXSymbols)
        return;

    currentChar = LitChar(); // get past SYMDEFSTART

    /* read the symbol name */
    p = sym.name;
    while ((*p++ = currentChar) != SYMDELIMIT)
        currentChar = LitChar();
    *--p = L'\0';
    if (p - sym.name > MAX_SYMBOL) {
        ParseError1(2247);
        return;
    }
    currentChar = LitChar(); /* read past the delimiter */

    p = szDefn;
    while ((*p++ = currentChar) != SYMDELIMIT)
        currentChar = LitChar();
    *--p = L'\0';
    currentChar = LitChar(); /* read past the delimiter */

    sym.file[0] = L'\0';

    p = szLine;
    while ((*p++ = currentChar) != SYMDELIMIT)
        currentChar = LitChar();
    *--p = L'\0';
    sym.line = (WORD)wcsatoi(szLine);
    currentChar = LitChar(); /* read past the delimiter */

    flags = (CHAR)currentChar;
    flags &= 0x7f; // clear the hi bit
    currentChar = LitChar(); /* read past the delimiter */

    /* leave positioned at last character (LitChar will bump) */
    if (currentChar != SYMDELIMIT) {
        ParseError1(2248);
    }

    WriteSymbolDef(sym.name, szDefn, sym.file, sym.line, flags);
}


/*---------------------------------------------------------------------------*/
/*                                                                           */
/* GetSymbol() - read a symbol and put id in the token if there              */
/*                                                                           */
/*---------------------------------------------------------------------------*/
void
GetSymbol(
    int fReportError,
    WCHAR curChar
    )
{
    WCHAR currentChar = curChar;

    token.sym.name[0] = L'\0';
    token.sym.file[0] = L'\0';
    token.sym.line = 0;

    if (!fAFXSymbols)
        return;

    /* skip whitespace */
    while (iswhite(currentChar))
        currentChar = LitChar();

    if (currentChar == SYMUSESTART) {
        WCHAR * p;
        int i = 0;
        WCHAR szLine[16];

        currentChar = LitChar(); // get past SYMUSESTART

        if (currentChar != L'\"') {
            ParseError1(2249);
            return;
        }
        currentChar = LitChar(); // get past the first \"

        /* read the symbol name */
        p = token.sym.name;
        while ((*p++ = currentChar) != SYMDELIMIT)
            currentChar = LitChar();
        *--p = L'\0';
        if (p - token.sym.name > MAX_SYMBOL) {
            ParseError1(2247);
            return;
        }
        currentChar = LitChar(); /* read past the delimiter */

        p = token.sym.file;
        while ((*p++ = currentChar) != SYMDELIMIT)
            currentChar = LitChar();
        *--p = L'\0';
        currentChar = LitChar(); /* read past the delimiter */

        p = szLine;
        while ((*p++ = currentChar) != L'\"')
            currentChar = LitChar();
        *--p = L'\0';
        token.sym.line = (WORD)wcsatoi(szLine);

        if (currentChar != L'\"') {
            ParseError1(2249);
            return;
        }

        currentChar = LitChar(); // get past SYMDELIMIT

        /* skip whitespace */
        while (iswhite(currentChar))
            currentChar = LitChar();
    }
}