
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993-1996 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

/****************************** Module Header *******************************
* Module Name: resutil.c
*
* Contains utility functions for working with the Windows resource file.
*
* Functions:
*   ParseDialogBoxHeader()
*   ParseControlData()
*   DWordAlign()
*   DWordPad()
*   ResourceType)
*   ResourceName()
*   ResourcePart2()
*   ResourceSize()
*   SkipResHeader()
*   SkipSz()
*   SkipDialogBoxHeader()
*   SkipControlData()
*   NameOrdCpy()
*   NameOrdCmp()
*   NameOrdLen()
*   NameOrdDup()
*   StrToNameOrd()
*   WriteOrd()
*   WriteResHeader()
*   WriteSz()
*
****************************************************************************/

#include "dlgedit.h"
#include "dlgfuncs.h"
#include "dlgextrn.h"

#include <string.h>
#include <wchar.h>



/************************************************************************
* ParseDialogBoxHeader
*
*
* Arguments:
*   PDIALOGBOXHEADER pdbh - Points to dialog box header to parse.
*   PDWORD pflStyle       - Receives the style.
*   PDWORD pflExtStyle    - Receives the extended style.
*   PINT pcdit            - Receives the number of controls in the dialog.
*   PINT px               - Receives starting x location.
*   PINT py               - Receives starting y location.
*   PINT pcx              - Receives the width.
*   PINT pcy              - Receives the height.
*   LPTSTR *ppszMenuName  - Receives the menu name.
*   LPTSTR *ppszClass     - Receives the class name.
*   LPTSTR *ppszCaption   - Receives the caption.
*   PINT pPointSize       - Receives the point size.
*   LPTSTR *ppszFontName  - Receives the font name.
*
* Returns:
*   A pointer to the first dialog item past the dialog template header.
*
************************************************************************/

PCONTROLDATA ParseDialogBoxHeader(
    PDIALOGBOXHEADER pdbh,
    PDWORD pflStyle,
    PDWORD pflExtStyle,
    PINT pcdit,
    PINT px,
    PINT py,
    PINT pcx,
    PINT pcy,
    LPTSTR *ppszMenuName,
    LPTSTR *ppszClass,
    LPTSTR *ppszCaption,
    PINT pPointSize,
    LPTSTR *ppszFontName)
{
    BYTE UNALIGNED *pb;

    *pflStyle = pdbh->lStyle;
    *pflExtStyle = pdbh->lExtendedStyle;
    *pcdit = pdbh->NumberOfItems;
    *px = (SHORT)pdbh->x;
    *py = (SHORT)pdbh->y;
    *pcx = (SHORT)pdbh->cx;
    *pcy = (SHORT)pdbh->cy;

    pb = (PBYTE)pdbh + SIZEOF_DIALOGBOXHEADER;
    *ppszMenuName = (LPTSTR)pb;
    pb += NameOrdLen((LPTSTR)pb);

    *ppszClass = (LPTSTR)pb;
    pb += NameOrdLen((LPTSTR)pb);

    *ppszCaption = (LPTSTR)pb;
    pb += (lstrlen((LPTSTR)pb) + 1) * sizeof(TCHAR);

    /*
     * Does the template specify a font?
     */
    if (pdbh->lStyle & DS_SETFONT) {
        *pPointSize = (SHORT)(*(PWORD)pb);
        pb += sizeof(WORD);
        *ppszFontName = (LPTSTR)pb;
        pb += (lstrlen((LPTSTR)pb) + 1) * sizeof(TCHAR);
    }
    else {
        *pPointSize = 0;
        *ppszFontName = NULL;
    }

    DWordAlign((PBYTE *)&pb);

    return (PCONTROLDATA)pb;
}



/************************************************************************
* ParseControlData
*
*
* Arguments:
*   PCONTROLDATA pcd    - Points to the control data to parse.
*   PDWORD pflStyle     - Receives the control style.
*   PDWORD pflExtStyle  - Receives the extended control style.
*   PINT px             - Receives starting x location.
*   PINT py             - Receives starting y location.
*   PINT pcx            - Receives the width.
*   PINT pcy            - Receives the height.
*   PINT pid            - Receives the control id.
*   LPTSTR *ppszClass   - Receives the class name.
*   LPTSTR *ppszText    - Receives the text.
*
* Returns:
*   A pointer to the next dialog item past the given one.
*
************************************************************************/

PCONTROLDATA ParseControlData(
    PCONTROLDATA pcd,
    PDWORD pflStyle,
    PDWORD pflExtStyle,
    PINT px,
    PINT py,
    PINT pcx,
    PINT pcy,
    PINT pid,
    LPTSTR *ppszClass,
    LPTSTR *ppszText)
{
    BYTE UNALIGNED *pb;

    *pflStyle = pcd->lStyle;
    *pflExtStyle = pcd->lExtendedStyle;

    *px = (SHORT)pcd->x;
    *py = (SHORT)pcd->y;
    *pcx = (SHORT)pcd->cx;
    *pcy = (SHORT)pcd->cy;
    *pid = (SHORT)pcd->wId;

    pb = (PBYTE)pcd + SIZEOF_CONTROLDATA;
    *ppszClass = (LPTSTR)pb;
    pb += NameOrdLen((LPTSTR)pb);

    *ppszText = (LPTSTR)pb;
    pb += NameOrdLen((LPTSTR)pb);

    /*
     * Finally, skip the Create Struct Data.
     * After this, pb will be pointing to the next control.
     */
    pb += *(PWORD)pb + sizeof(WORD);

    DWordAlign((PBYTE *)&pb);

    return (PCONTROLDATA)pb;
}



/************************************************************************
* DWordAlign
*
* This function aligns the passed pointer to a DWORD boundary.
*
* Arguments:
*   PBYTE *ppb - Points to the pointer to align.
*
************************************************************************/

VOID DWordAlign(
    PBYTE *ppb)
{
    *ppb += (4 - (((WORD)(DWORD)*ppb) & 3)) % 4;
}



/************************************************************************
* DWordPad
*
* This function aligns the passed pointer to a DWORD boundary, padding
* with nulls as it goes.
*
* Arguments:
*   PBYTE *ppb - Points to the pointer to align.
*
************************************************************************/

VOID DWordPad(
    PBYTE *ppb)
{
    WORD cbytes;

    cbytes = (WORD)((4 - (((WORD)(DWORD)*ppb) & 3)) % 4);
    while (cbytes) {
        *((*ppb)++) = 0;
        cbytes--;
    }
}



/************************************************************************
* ResourceType
*
* This function returns a pointer to the type of the resource.
* The type can be either a string or an ordinal.
*
* Arguments:
*   PRES pRes - Points to the start of the resource.
*
* Returns:
*     Pointer to the type of the resource.
*
************************************************************************/

LPTSTR ResourceType(
    PRES pRes)
{
    /*
     * Skip past the two size fields.
     */
    return (LPTSTR)((PBYTE)pRes + sizeof(DWORD) + sizeof(DWORD));
}



/************************************************************************
* ResourceName
*
* This function returns a pointer to the name of the resource.
* The name can be either a string or an ordinal.
*
* Arguments:
*   PRES pRes - Points to the start of the resource.
*
* Returns:
*     Pointer to the name of the resource.
*
************************************************************************/

LPTSTR ResourceName(
    PRES pRes)
{
    PBYTE pb;

    /*
     * Skip past the two size fields.
     */
    pb = (PBYTE)pRes + sizeof(DWORD) + sizeof(DWORD);

    /*
     * Skip past the "Type" field to the name.
     */
    return (LPTSTR)SkipSz((LPTSTR)pb);
}



/************************************************************************
* ResourcePart2
*
* This function returns a pointer to the second half of the resource
* header.
*
* Arguments:
*   PRES pRes - Points to the start of the resource.
*
* Returns:
*   A pointer to the second part of the resource header.
*
************************************************************************/

PRES2 ResourcePart2(
    PRES pRes)
{
    PBYTE pb;

    /*
     * Skip past the first part of the resource header.
     */
    pb = (PBYTE)pRes + sizeof(RES);

    /*
     * Skip past the "Type" field to the name.
     */
    pb = SkipSz((LPTSTR)pb);

    /*
     * Skip past the name field also.
     */
    pb = SkipSz((LPTSTR)pb);
    DWordAlign(&pb);

    return (PRES2)pb;
}



/************************************************************************
* ResourceSize
*
* This returns the size of the given resource.
*
* Arguments:
*   PRES pRes - Points to the start of the resource.
*
* Returns:
*   Size of the resource, including the header.
*
************************************************************************/

DWORD ResourceSize(
    PRES pRes)
{
    return pRes->HeaderSize + pRes->DataSize;
}



/************************************************************************
* SkipResHeader
*
* This function returns a pointer to the start of the resource data,
* just past it's header.
*
* Arguments:
*   PRES pRes - Pointer to the resource.
*
************************************************************************/

PBYTE SkipResHeader(
    PRES pRes)
{
    return (PBYTE)pRes + pRes->HeaderSize;
}



/************************************************************************
* SkipSz
*
* This function skips past a string and returns a pointer to just
* past it.  It detects if the string is really an ordinal and skips
* past these also.
*
* Arguments:
*   LPTSTR pNameOrd - Pointer to the string/ordinal.
*
************************************************************************/

PBYTE SkipSz(
    LPTSTR pNameOrd)
{
    if (IsOrd(pNameOrd))
        pNameOrd = (LPTSTR)((PBYTE)pNameOrd + sizeof(ORDINAL));
    else
        pNameOrd += lstrlen(pNameOrd) + 1;

    return (PBYTE)pNameOrd;
}



/************************************************************************
* SkipDialogBoxHeader
*
* This function skips past a dialog template structure and returns
* a pointer to the first dialog item template just past it.
*
* Arguments:
*   PDIALOGBOXHEADER pdbh - Points to the dialog box header.
*
* Returns:
*   A pointer to the first dialog item control data in the resource,
*   just past the dialog box header that was skipped.
*
************************************************************************/

PCONTROLDATA SkipDialogBoxHeader(
    PDIALOGBOXHEADER pdbh)
{
    BYTE UNALIGNED *pb;

    /*
     * Skip the fixed portion.
     */
    pb = (PBYTE)pdbh + SIZEOF_DIALOGBOXHEADER;

    /*
     * Skip the menu.
     */
    pb += NameOrdLen((LPTSTR)pb);

    /*
     * Skip the class.
     */
    pb += NameOrdLen((LPTSTR)pb);

    /*
     * Skip the caption.
     */
    pb += (lstrlen((LPTSTR)pb) + 1) * sizeof(TCHAR);

    /*
     * Does the template specify a font?
     */
    if (pdbh->lStyle & DS_SETFONT) {
        pb += sizeof(WORD);
        pb += (lstrlen((LPTSTR)pb) + 1) * sizeof(TCHAR);
    }

    DWordAlign((PBYTE *)&pb);

    return (PCONTROLDATA)pb;
}



/************************************************************************
* SkipControlData
*
* This function skips past the given control data to the next control.
*
* Arguments:
*   PCONTROLDATA pcd - Points to the control data structure to skip.
*
* Returns:
*   A pointer to the next control data structure.
*
************************************************************************/

PCONTROLDATA SkipControlData(
    PCONTROLDATA pcd)
{
    BYTE UNALIGNED *pb;

    /*
     * Skip the fixed portion.
     */
    pb = (PBYTE)pcd + SIZEOF_CONTROLDATA;

    /*
     * Skip the class.
     */
    pb += NameOrdLen((LPTSTR)pb);

    /*
     * Skip the text.
     */
    pb += NameOrdLen((LPTSTR)pb);

    /*
     * Finally, skip the Create Struct Data.
     * After this, pb will be pointing to the next control.
     */
    pb += *(PWORD)pb + sizeof(WORD);

    DWordAlign((PBYTE *)&pb);

    return (PCONTROLDATA)pb;
}



/************************************************************************
* NameOrdCpy
*
* This function copies a string or ordinal.  This function needs to be
* used whenever a string could possibly be an ordinal.  It returns a
* pointer to the first byte after the copied name/ordinal.
*
* Arguments:
*   LPTSTR pNameOrdDest - The destination buffer.
*   LPTSTR pNameOrdSrc  - The source string or ordinal.
*
************************************************************************/

PBYTE NameOrdCpy(
    LPTSTR pNameOrdDest,
    LPTSTR pNameOrdSrc)
{
    if (IsOrd(pNameOrdSrc)) {
        memcpy((PBYTE)pNameOrdDest, (PBYTE)pNameOrdSrc, sizeof(ORDINAL));
        return (PBYTE)pNameOrdDest + sizeof(ORDINAL);
    }
    else {
        lstrcpy(pNameOrdDest, pNameOrdSrc);
        return (PBYTE)(pNameOrdDest + (lstrlen(pNameOrdDest) + 1));
    }
}



/************************************************************************
* NameOrdCmp
*
* This function compares two strings or ordinals.  It returns a
* zero if they are equal, or non-zero if they are not.  This
* follows the convention of lstrcmp(), but the return should
* not be relied upon to determine which is "greater" than
* the other.
*
* Arguments:
*   LPTSTR pNameOrd1 - The first string or ordinal.
*   LPTSTR pNameOrd2 - The second string or ordinal.
*
************************************************************************/

INT NameOrdCmp(
    LPTSTR pNameOrd1,
    LPTSTR pNameOrd2)
{
    BOOL fIsOrd1;
    BOOL fIsOrd2;

    fIsOrd1 = IsOrd(pNameOrd1);
    fIsOrd2 = IsOrd(pNameOrd2);

    if (fIsOrd1 != fIsOrd2)
        return 1;

    if (fIsOrd1)
        return memcmp((PBYTE)pNameOrd1, (PBYTE)pNameOrd2, sizeof(ORDINAL));
    else
        return lstrcmp(pNameOrd1, pNameOrd2);
}



/************************************************************************
* NameOrdLen
*
* This function returns the length of a string or ordinal.
* If the given name is a string, the length of the string
* plus the terminating null is returned.  Otherwise,
* the size of an ORDINAL structure is returned.
*
* The length returned is in bytes, not wide-chars.
*
* Arguments:
*   LPTSTR pNameOrd - The string or ordinal.
*
************************************************************************/

INT NameOrdLen(
    LPTSTR pNameOrd)
{
    if (IsOrd(pNameOrd))
        return sizeof(ORDINAL);
    else
        return (lstrlen(pNameOrd) + 1) * sizeof(TCHAR);
}



/****************************************************************************
* NameOrdDup
*
* This function allocates a copy of the given name or ordinal.
*
* Arguments:
*   LPTSTR pNameOrd - The name or ordinal to duplicate.
*
* Returns a pointer to the new copy if successful, NULL if it fails.
*
****************************************************************************/

LPTSTR NameOrdDup(
    LPTSTR pNameOrd)
{
    register INT iLen;
    LPTSTR psz;

    iLen = NameOrdLen(pNameOrd);

    if (!(psz = (LPTSTR)MyAlloc(iLen)))
        return NULL;

    NameOrdCpy(psz, pNameOrd);

    return psz;
}



/************************************************************************
* StrToNameOrd
*
* This function takes the given string, determines if it is
* all numeric and if so, converts it in place into an ordinal.
* It is used to convert the string from an edit field for
* a value that can be an ordinal, such as the dialog name or
* an icon's text.
*
* Note that the pszNameOrd buffer must be large enough for an
* ordinal in case the string gets converted to an ordinal.
*
* Arguments:
*   LPTSTR pszNameOrd  - On input, contains the string to possibly
*                        convert.  On output, it will contain the
*                        original string or the string converted to
*                        an ordinal.
*   BOOL fDecOnly      - TRUE if hex values and negative values (the
*                        string starts with a '-') are not allowed.
*                        This flag prevents these types of strings
*                        from being candidates for conversion to
*                        ordinals.
*
************************************************************************/

VOID StrToNameOrd(
    LPTSTR pszNameOrd,
    BOOL fDecOnly)
{
    register INT i;
    INT nOrd;
    INT nLen;

    /*
     * Empty string?
     */
    if (!(*pszNameOrd))
        return;

    nLen = lstrlen(pszNameOrd);

    /*
     * Is a hex value ok and does this appear to be a hex value?
     */
    if (!fDecOnly && pszNameOrd[0] == CHAR_0 &&
            (pszNameOrd[1] == CHAR_X || pszNameOrd[1] == CHAR_CAP_X)) {
        for (i = 2; i < nLen; i++) {
            if (!iswxdigit(pszNameOrd[i]))
                return;
        }

        nOrd =  axtoi(&pszNameOrd[2]);
    }
    else {
        /*
         * All characters must be numeric.  Negative numbers may
         * or may not be allowed, based on the fDecOnly flag.
         */
        for (i = 0; i < nLen; i++) {
            if (!iswdigit(pszNameOrd[i]) &&
                    (fDecOnly || i != 0 || pszNameOrd[0] != CHAR_MINUS))
                return;
        }

        nOrd = awtoi(pszNameOrd);
    }

    /*
     * Return the ordinal in the original buffer.
     */
    WriteOrd((PORDINAL)pszNameOrd, nOrd);
}



/************************************************************************
* WriteOrd
*
* This function writes out the given ordinal to the specified
* memory location.  It returns the first byte past the newly
* written ordinal.
*
* Arguments:
*   PORDINAL pMem   - Pointer to the location to write the ordinal.
*   INT nOrdinalID  - Ordinal ID to write.
*
************************************************************************/

PBYTE WriteOrd(
    PORDINAL pOrd,
    INT nOrdinalID)
{
    pOrd->wReserved = 0xffff;
    pOrd->wOrdID = (WORD)nOrdinalID;

    return (PBYTE)pOrd + sizeof(ORDINAL);
}



/************************************************************************
* WriteResHeader
*
* This function writes out a resource header to the memory location
* specified.
*
* Arguments:
*
*    PRES pRes            - pointer to the resource 
*    DWORD DataSize       - size of the data
*    INT iResType         - resource type
*    LPTSTR pszResName    - resource name
*    WORD fResFlags       - resource flags
*    WORD LanguageId      - language id
*    DWORD DataVersion    - data version
*    DWORD Version        - version
*    DWORD Characteristics-characterstics
*
* Returns:
*    number of bytes written
*
************************************************************************/

PBYTE WriteResHeader(
    PRES pRes,
    DWORD DataSize,
    INT iResType,
    LPTSTR pszResName,
    WORD fResFlags,
    WORD LanguageId,
    DWORD DataVersion,
    DWORD Version,
    DWORD Characteristics)
{
    PBYTE pb;
    PRES2 pRes2;

    pb = (PBYTE)pRes + sizeof(RES);
    pb = WriteOrd((PORDINAL)pb, iResType);
    pb = NameOrdCpy((LPTSTR)pb, pszResName);
    DWordPad(&pb);

    pRes->DataSize = DataSize;
    pRes->HeaderSize = (pb - (PBYTE)pRes) + sizeof(RES2);

    pRes2 = (PRES2)pb;
    pRes2->DataVersion = DataVersion;
    pRes2->MemoryFlags = fResFlags;
    pRes2->LanguageId = LanguageId;
    pRes2->Version = Version;
    pRes2->Characteristics = Characteristics;

    return (PBYTE)pRes + pRes->HeaderSize;
}



/************************************************************************
* WriteSz
*
* This function writes out the string given to the specified
* memory location.  It returns the first byte past the newly
* written string.
*
* Arguments:
*   LPTSTR pszDest - Pointer to the location to write the string.
*   LPTSTR pszSrc  - The string to write.
*
************************************************************************/

PBYTE WriteSz(
    LPTSTR pszDest,
    LPTSTR pszSrc)
{
    while (*pszSrc)
        *pszDest++ = *pszSrc++;

    *pszDest++ = CHAR_NULL;

    return (PBYTE)pszDest;
}

