RESUTIL.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 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;
}