660 lines
18 KiB
C
660 lines
18 KiB
C
/****************************************************************************/
|
|
/* */
|
|
/* Microsoft Confidential */
|
|
/* */
|
|
/* Copyright (c) Microsoft Corp. 1987, 1990 */
|
|
/* All Rights Reserved */
|
|
/* */
|
|
/****************************************************************************/
|
|
/****************************** Module Header *******************************
|
|
* Module Name: rwres.c
|
|
*
|
|
* Does all the reading and writing of the .RES (resource) file.
|
|
*
|
|
* History:
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "dlgedit.h"
|
|
#include "dlgfuncs.h"
|
|
#include "dlgextrn.h"
|
|
|
|
|
|
/*
|
|
* The bytes in the special RT_RESOURCE32 type resource that is the
|
|
* first resource in every Win32 format res file. The first 8 bytes
|
|
* in this resource's header were specially designed to be invalid
|
|
* for a 16 bit format resource file, so that tools can determine
|
|
* immediately if they are reading a 16 bit or a Win32 format res
|
|
* file.
|
|
*/
|
|
static BYTE abResource32[] = {
|
|
0x00, 0x00, 0x00, 0x00, // DataSize (0 bytes).
|
|
0x20, 0x00, 0x00, 0x00, // HeaderSize (32 bytes).
|
|
0xff, 0xff, 0x00, 0x00, // Type (RT_RESOURCE32).
|
|
0xff, 0xff, 0x00, 0x00, // Name (ordinal 0).
|
|
0x00, 0x00, 0x00, 0x00, // DataVersion
|
|
0x00, 0x00, // MemoryFlags
|
|
0x00, 0x00, // LanguageId
|
|
0x00, 0x00, 0x00, 0x00, // Version
|
|
0x00, 0x00, 0x00, 0x00 // Characteristics
|
|
};
|
|
|
|
|
|
STATICFN BOOL LoadResFile(HANDLE hfRes, LPTSTR pszFullResFile,
|
|
LPTSTR pszIncludeBuf);
|
|
STATICFN BOOL IsValidResFile(PRES pRes, INT cbFileSize);
|
|
STATICFN PRES SafeParseResHeader(PRES pRes, INT cbMaxSize);
|
|
STATICFN INT SafeNameOrdLen(LPTSTR psz, INT cbMaxLen);
|
|
STATICFN VOID SafeDWordAlign(PBYTE *ppb, PINT pcbMax);
|
|
STATICFN BOOL WriteDlgIncludeRes(HANDLE hfWrite, LPTSTR pszFullResFile);
|
|
|
|
|
|
|
|
/************************************************************************
|
|
* OpenResFile
|
|
*
|
|
* High level function to load the data in a resource file. The
|
|
* function LoadResFile is called to do the actual work, after
|
|
* this code does some housekeeping.
|
|
*
|
|
* Arguments:
|
|
* LPTSTR pszFullPath - The full path to the resource file.
|
|
*
|
|
* Side Effects:
|
|
* Might put up a message box.
|
|
* The current include file (if any) is free'd.
|
|
* szFullResFile is set to the full path.
|
|
* pszResFile is pointed to the file name portion of the path.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
BOOL OpenResFile(
|
|
LPTSTR pszFullPath)
|
|
{
|
|
HCURSOR hcurSave;
|
|
PRESLINK prl;
|
|
PRESLINK prlSave;
|
|
BOOL fSuccess = FALSE;
|
|
INT cDlg;
|
|
HANDLE hfRes;
|
|
TCHAR szInclude[CCHMAXPATH];
|
|
TCHAR szFullInclude[CCHMAXPATH];
|
|
BOOL fIncOpened = FALSE;
|
|
|
|
hcurSave = SetCursor(hcurWait);
|
|
|
|
/*
|
|
* Close any existing resource and include file and free memory.
|
|
* It is assumed that if either had been changed, the user was asked
|
|
* if they wanted to save them, because it is too late now.
|
|
*/
|
|
FreeRes();
|
|
FreeInclude();
|
|
|
|
if ((hfRes = CreateFile(pszFullPath, GENERIC_READ,
|
|
FILE_SHARE_READ, NULL, OPEN_EXISTING,
|
|
FILE_FLAG_SEQUENTIAL_SCAN, NULL)) != (HANDLE)-1 &&
|
|
LoadResFile(hfRes, pszFullPath, szInclude)) {
|
|
lstrcpy(szFullResFile, pszFullPath);
|
|
pszResFile = FileInPath(szFullResFile);
|
|
|
|
ShowFileStatus(TRUE);
|
|
|
|
/*
|
|
* If there was a DLGINCLUDE resource found, try and open the
|
|
* specified include file, after making sure that it is a
|
|
* fully formed path.
|
|
*/
|
|
if (*szInclude) {
|
|
/*
|
|
* Does the include filespec from the res file appear to
|
|
* be a simple filename without a path? If so, look for
|
|
* it in the same directory that the res file is in.
|
|
* Otherwise, assume it has a fully qualified path.
|
|
*/
|
|
if (!HasPath(szInclude)) {
|
|
lstrcpy(szFullInclude, szFullResFile);
|
|
lstrcpy(FileInPath(szFullInclude), szInclude);
|
|
}
|
|
else {
|
|
lstrcpy(szFullInclude, szInclude);
|
|
}
|
|
|
|
fIncOpened = OpenIncludeFile(szFullInclude);
|
|
}
|
|
|
|
/*
|
|
* If there wasn't an include resource found, or there was
|
|
* but it couldn't be opened, we want to ask the user for the
|
|
* include file to use for this resource file.
|
|
*/
|
|
if (!fIncOpened)
|
|
Open(FILE_INCLUDE);
|
|
|
|
/*
|
|
* Start counting the dialogs in the resource, but stop at two.
|
|
*/
|
|
cDlg = 0;
|
|
for (cDlg = 0, prl = gprlHead; prl; prl = prl->prlNext) {
|
|
if (prl->fDlgResource) {
|
|
if (++cDlg > 1)
|
|
break;
|
|
|
|
prlSave = prl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there are multiple dialogs, display the "Select Dialog"
|
|
* dialog to ask the user which one they want to edit. If
|
|
* there is exactly one dialog, just go ahead and show it
|
|
* initially.
|
|
*/
|
|
if (cDlg == 1)
|
|
ResLinkToDialog(prlSave);
|
|
else if (cDlg > 1)
|
|
SelectDialogDialog();
|
|
|
|
fSuccess = TRUE;
|
|
}
|
|
|
|
if (hfRes != (HANDLE)-1)
|
|
CloseHandle(hfRes);
|
|
|
|
ShowFileStatus(TRUE);
|
|
SetCursor(hcurSave);
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
* LoadResFile
|
|
*
|
|
* Loads the resource file specified by the passed in file handle.
|
|
* This function first verifies that it is a valid resource file.
|
|
*
|
|
* Arguments:
|
|
* HANDLE hfRes - File handle to read from.
|
|
* LPTSTR pszFullResFile - Full name of resource file being loaded.
|
|
* LPTSTR pszIncludeBuf - Where to return the include file name, if
|
|
* a DLGINCLUDE resource is found in the res
|
|
* file. If not, this buffer gets a null byte
|
|
* as its first character.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
STATICFN BOOL LoadResFile(
|
|
HANDLE hfRes,
|
|
LPTSTR pszFullResFile,
|
|
LPTSTR pszIncludeBuf)
|
|
{
|
|
HANDLE hAllRes;
|
|
PRES pRes;
|
|
PRES pResAll;
|
|
PRESLINK prl;
|
|
PRESLINK prlT;
|
|
INT cbRead;
|
|
LPTSTR pszResType;
|
|
DWORD cbFileSize;
|
|
|
|
cbFileSize = GetFileSize((HANDLE)hfRes, NULL);
|
|
|
|
if (!(hAllRes = GlobalAlloc(GMEM_MOVEABLE, cbFileSize))) {
|
|
Message(MSG_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
*pszIncludeBuf = CHAR_NULL;
|
|
pRes = pResAll = (PRES)GlobalLock(hAllRes);
|
|
if ((cbRead = _lread((HFILE)hfRes, (LPSTR)pResAll, cbFileSize)) != -1 &&
|
|
cbRead == (INT)cbFileSize) {
|
|
if (!IsValidResFile(pResAll, cbFileSize)) {
|
|
Message(MSG_BADRESFILE, pszFullResFile);
|
|
}
|
|
else do {
|
|
pszResType = ResourceType(pRes);
|
|
|
|
if (IsOrd(pszResType) && OrdID(pszResType) == ORDID_RT_DLGINCLUDE) {
|
|
/*
|
|
* Pass back the include file name. This resource
|
|
* will not be saved in the res list because it is
|
|
* going to be explicitly written out later if
|
|
* necessary.
|
|
*/
|
|
NameOrdCpy(pszIncludeBuf, (LPTSTR)SkipResHeader(pRes));
|
|
}
|
|
else if (IsOrd(pszResType) &&
|
|
OrdID(pszResType) == ORDID_RT_RESOURCE32) {
|
|
/*
|
|
* This is the dummy resource that identifies a
|
|
* 32 bit resource file. This resource should be
|
|
* skipped also.
|
|
*/
|
|
}
|
|
else {
|
|
/*
|
|
* This is some other kind of a resource.
|
|
* Save it in the resource list.
|
|
*/
|
|
if (!(prlT = AllocResLink(pRes))) {
|
|
FreeResList();
|
|
break;
|
|
}
|
|
|
|
if (!gprlHead) {
|
|
gprlHead = prl = prlT;
|
|
}
|
|
else {
|
|
prl->prlNext = prlT;
|
|
prl = prlT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Move to the next resource.
|
|
*/
|
|
pRes = (PRES)((PBYTE)pRes + pRes->HeaderSize + pRes->DataSize);
|
|
DWordAlign((PBYTE *)&pRes);
|
|
} while (pRes < (PRES)((PBYTE)pResAll + cbFileSize));
|
|
}
|
|
|
|
GlobalUnlock(hAllRes);
|
|
GlobalFree(hAllRes);
|
|
|
|
return (gprlHead ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
* IsValidResFile
|
|
*
|
|
* This function does some basic checks on the resource file in memory
|
|
* pointed to by pbRes. It does this by walking through the resource
|
|
* checking for the resource header info and lengths.
|
|
*
|
|
* Returns:
|
|
* TRUE if it is a valid resource file, FALSE if not.
|
|
*
|
|
* Arguments:
|
|
* PRES pRes - Pointer to the first resource in the file.
|
|
* INT cbFileSize - Size of the file in memory.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
STATICFN BOOL IsValidResFile(
|
|
PRES pRes,
|
|
INT cbFileSize)
|
|
{
|
|
INT cbCurLoc = 0;
|
|
PRES pResT;
|
|
|
|
/*
|
|
* The file is zero size.
|
|
*/
|
|
if (!cbFileSize)
|
|
return FALSE;
|
|
|
|
pResT = pRes;
|
|
while (cbCurLoc < cbFileSize) {
|
|
/*
|
|
* Check this resource for validity.
|
|
*/
|
|
if (!(pResT = SafeParseResHeader(pResT, cbFileSize - cbCurLoc)))
|
|
return FALSE;
|
|
|
|
/*
|
|
* Point just past the resource that was just checked.
|
|
*/
|
|
cbCurLoc = (PBYTE)pResT - (PBYTE)pRes;
|
|
}
|
|
|
|
return (cbCurLoc == cbFileSize) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
* SafeParseResHeader
|
|
*
|
|
* This function parses the specified resource header and returns a
|
|
* pointer to the next resource header in the resource file. It does
|
|
* it in a safe manner, not touching memory beyond the maximum size
|
|
* specified. If the resource header is somehow messed up and
|
|
* specifies a size that is larger than will fit in the given maximum
|
|
* size, NULL is returned.
|
|
*
|
|
* Returns:
|
|
* A pointer to just past this resource, or NULL if the resource
|
|
* is larger than cbMaxSize.
|
|
*
|
|
* Arguments:
|
|
* PRES pRes - Pointer to the resource.
|
|
* INT cbMaxSize - Maximum size the resource can be.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
STATICFN PRES SafeParseResHeader(
|
|
PRES pRes,
|
|
INT cbMaxSize)
|
|
{
|
|
INT cbLen;
|
|
DWORD cbDataSize;
|
|
PBYTE pb;
|
|
|
|
pb = (PBYTE)pRes;
|
|
|
|
/*
|
|
* There must be room for the first part of the resource header.
|
|
*/
|
|
if (sizeof(RES) > cbMaxSize)
|
|
return FALSE;
|
|
|
|
pb += sizeof(RES);
|
|
cbMaxSize -= sizeof(RES);
|
|
|
|
/*
|
|
* Parse the type field then skip over it.
|
|
*/
|
|
cbLen = SafeNameOrdLen((LPTSTR)pb, cbMaxSize);
|
|
if (cbLen > cbMaxSize)
|
|
return NULL;
|
|
|
|
pb += cbLen;
|
|
cbMaxSize -= cbLen;
|
|
|
|
/*
|
|
* Parse the name field then skip over it.
|
|
*/
|
|
cbLen = SafeNameOrdLen((LPTSTR)pb, cbMaxSize);
|
|
if (cbLen > cbMaxSize)
|
|
return NULL;
|
|
|
|
pb += cbLen;
|
|
cbMaxSize -= cbLen;
|
|
|
|
SafeDWordAlign(&pb, &cbMaxSize);
|
|
|
|
/*
|
|
* There must be room for the second part of the resource header.
|
|
*/
|
|
if (sizeof(RES2) > cbMaxSize)
|
|
return FALSE;
|
|
|
|
pb += sizeof(RES2);
|
|
cbMaxSize -= sizeof(RES2);
|
|
|
|
/*
|
|
* The header size field must be valid.
|
|
*/
|
|
if (pRes->HeaderSize != (DWORD)(pb - (PBYTE)pRes))
|
|
return FALSE;
|
|
|
|
/*
|
|
* Calculate the size of the data, taking into account any
|
|
* padding that may be at the end to make it DWORD aligned.
|
|
*/
|
|
cbDataSize = pRes->DataSize;
|
|
DWordAlign((PBYTE *)&cbDataSize);
|
|
|
|
/*
|
|
* There must be room enough left for the data.
|
|
*/
|
|
if (cbDataSize > (DWORD)cbMaxSize)
|
|
return FALSE;
|
|
|
|
return (PRES)(pb + cbDataSize);
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
* SafeNameOrdLen
|
|
*
|
|
* This function returns the size of the specified name/ordinal. It
|
|
* does it in a safe manner, not touching memory beyond the specified
|
|
* maximum size. If it is a string and the terminating null is not
|
|
* found within cbMaxLen bytes, then cbMaxLen plus one is returned.
|
|
*
|
|
* Returns:
|
|
* The length of the ordinal if it is an ordinal, or the length
|
|
* of the string (plus the null terminator) if it is a string.
|
|
*
|
|
* Arguments:
|
|
* LPTSTR psz - Pointer to the name/ordinal.
|
|
* INT cbMaxLen - Maximum length to probe.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
STATICFN INT SafeNameOrdLen(
|
|
LPTSTR psz,
|
|
INT cbMaxLen)
|
|
{
|
|
INT cbLen = 0;
|
|
|
|
if (cbMaxLen == 0)
|
|
return 1;
|
|
|
|
if (IsOrd(psz))
|
|
return sizeof(ORDINAL);
|
|
|
|
for (cbLen = 0; cbLen < cbMaxLen && *psz; psz++, cbLen += sizeof(TCHAR))
|
|
;
|
|
|
|
/*
|
|
* Account for the null terminator.
|
|
*/
|
|
cbLen += sizeof(TCHAR);
|
|
|
|
return cbLen;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
* SafeDWordAlign
|
|
*
|
|
* This function aligns the passed pointer to a DWORD boundary. At the
|
|
* same time, it subtracts from the specified counter the amount that
|
|
* it had to add to the pointer, if any.
|
|
*
|
|
* Arguments:
|
|
* PBYTE *ppb - Points to the pointer to align.
|
|
* PINT pcbMax - Points to the current count to decrement.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
STATICFN VOID SafeDWordAlign(
|
|
PBYTE *ppb,
|
|
PINT pcbMax)
|
|
{
|
|
INT cbAlign;
|
|
|
|
cbAlign = (4 - (((WORD)(DWORD)*ppb) & 3)) % 4;
|
|
*ppb += cbAlign;
|
|
*pcbMax -= cbAlign;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
* WriteRes
|
|
*
|
|
* Worker routine that does the actual writing out of the resource data.
|
|
*
|
|
* Arguments:
|
|
* HANDLE hfWrite - Resource file to write to.
|
|
* LPTSTR pszFullResFile - Full pathname to the resource file that
|
|
* is being written.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
BOOL WriteRes(
|
|
HANDLE hfWrite,
|
|
LPTSTR pszFullResFile)
|
|
{
|
|
PRESLINK prl;
|
|
PRES pRes;
|
|
|
|
/*
|
|
* Write the special RT_RESOURCE32 dummy resource to the beginning
|
|
* of the resource file. This resource is aligned, so no padding
|
|
* needs to be done before writing the resource that follows it.
|
|
*/
|
|
if (_lwrite((HFILE)hfWrite, abResource32, sizeof(abResource32)) == -1)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Write out any DLGINCLUDE resource there may be.
|
|
*/
|
|
if (!WriteDlgIncludeRes(hfWrite, pszFullResFile))
|
|
return FALSE;
|
|
|
|
/*
|
|
* Loop through all the resources.
|
|
*/
|
|
for (prl = gprlHead; prl; prl = prl->prlNext) {
|
|
if (!(pRes = (PRES)GlobalLock(prl->hRes)))
|
|
return FALSE;
|
|
|
|
/*
|
|
* Write the actual data.
|
|
*/
|
|
if (_lwrite((HFILE)hfWrite, (LPSTR)pRes, prl->cbRes) == -1)
|
|
return FALSE;
|
|
|
|
/*
|
|
* Write pads out to the next DWORD boundary.
|
|
*/
|
|
if (!WriteDWordPad(hfWrite, prl->cbRes))
|
|
return FALSE;
|
|
|
|
GlobalUnlock(prl->hRes);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
/************************************************************************
|
|
* WriteDlgIncludeRes
|
|
*
|
|
* Writes out a DLGINCLUDE resource to the specified resource file for
|
|
* the currently open include file.
|
|
*
|
|
* Arguments:
|
|
* HANDLE hfWrite - Resource file handle to write to.
|
|
* LPTSTR pszFullResFile - Full pathname to the resource file that
|
|
* is being written.
|
|
*
|
|
* Returns:
|
|
* Number of characters written if the include resource was
|
|
* written successfully (or there wasn't one to write) or -1
|
|
* if an error occurred.
|
|
*
|
|
* History:
|
|
*
|
|
************************************************************************/
|
|
|
|
STATICFN BOOL WriteDlgIncludeRes(
|
|
HANDLE hfWrite,
|
|
LPTSTR pszFullResFile)
|
|
{
|
|
INT cbResSize;
|
|
INT cbDataSize;
|
|
PRES pResBegin;
|
|
PBYTE pb;
|
|
INT cbWritten;
|
|
LPTSTR pszInc;
|
|
ORDINAL ordDlgIncName;
|
|
BOOL fSuccess = FALSE;
|
|
|
|
/*
|
|
* No include file. Do nothing (return success).
|
|
*/
|
|
if (!pszIncludeFile)
|
|
return TRUE;
|
|
|
|
/*
|
|
* If the include file is in a different directory than the resource
|
|
* file, write the full path to it. Otherwise, we just write the
|
|
* include file name.
|
|
*/
|
|
if (DifferentDirs(pszFullResFile, szFullIncludeFile))
|
|
pszInc = szFullIncludeFile;
|
|
else
|
|
pszInc = pszIncludeFile;
|
|
|
|
/*
|
|
* The DLGINCLUDE resource name always is the same (a value of 1).
|
|
*/
|
|
WriteOrd(&ordDlgIncName, ORDID_DLGINCLUDE_NAME);
|
|
|
|
/*
|
|
* Determine the size of the resource data.
|
|
*/
|
|
cbDataSize = NameOrdLen(pszInc);
|
|
|
|
/*
|
|
* Determine the resource size. Note that there is no need for
|
|
* DWORD padding after the res header, because the header will
|
|
* be aligned (there are no strings in it).
|
|
*/
|
|
cbResSize = sizeof(RES) + // First part of res header.
|
|
sizeof(ORDINAL) + // Type ordinal.
|
|
sizeof(ORDINAL) + // Name ordinal.
|
|
sizeof(RES2) + // Second half of header.
|
|
cbDataSize; // Size of data.
|
|
|
|
if (!(pResBegin = (PRES)MyAlloc(cbResSize)))
|
|
return FALSE;
|
|
|
|
/*
|
|
* Write the resource header.
|
|
*/
|
|
pb = WriteResHeader(pResBegin, cbDataSize, ORDID_RT_DLGINCLUDE,
|
|
(LPTSTR)&ordDlgIncName, MMF_MOVEABLE | MMF_PURE | MMF_DISCARDABLE,
|
|
0, 0, 0, 0);
|
|
|
|
/*
|
|
* Write the resource data. This is simply the name
|
|
* of the include file.
|
|
*/
|
|
NameOrdCpy((LPTSTR)pb, pszInc);
|
|
|
|
/*
|
|
* Write the resource to the file.
|
|
*/
|
|
cbWritten = _lwrite((HFILE)hfWrite, (LPSTR)pResBegin, cbResSize);
|
|
|
|
if (cbWritten == cbResSize) {
|
|
/*
|
|
* Write pads out to the next DWORD boundary.
|
|
*/
|
|
if (WriteDWordPad(hfWrite, cbWritten))
|
|
fSuccess = TRUE;
|
|
}
|
|
|
|
MyFree(pResBegin);
|
|
|
|
return fSuccess;
|
|
}
|