home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / winnt / dlgedit / rwres.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  19KB  |  661 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /****************************** Module Header *******************************
  13. * Module Name: rwres.c
  14. *
  15. * Does all the reading and writing of the .RES (resource) file.
  16. *
  17. * Functions:
  18. *
  19. *    OpenResFile()
  20. *    WriteRes()
  21. *    LoadResFile()
  22. *    IsValidResFile()
  23. *    SafeParseResHeader()
  24. *    SafeNameOrdLen()
  25. *    SafeDWordAlign()
  26. *    WriteDlgIncludeRes()
  27. *
  28. * Comments:
  29. *
  30. ****************************************************************************/
  31.  
  32. #include "dlgedit.h"
  33. #include "dlgfuncs.h"
  34. #include "dlgextrn.h"
  35.  
  36.  
  37. /*
  38.  * The bytes in the special RT_RESOURCE32 type resource that is the
  39.  * first resource in every Win32 format res file.  The first 8 bytes
  40.  * in this resource's header were specially designed to be invalid
  41.  * for a 16 bit format resource file, so that tools can determine
  42.  * immediately if they are reading a 16 bit or a Win32 format res
  43.  * file.
  44.  */
  45. static BYTE abResource32[] = {
  46.     0x00, 0x00, 0x00, 0x00,                 // DataSize (0 bytes).
  47.     0x20, 0x00, 0x00, 0x00,                 // HeaderSize (32 bytes).
  48.     0xff, 0xff, 0x00, 0x00,                 // Type (RT_RESOURCE32).
  49.     0xff, 0xff, 0x00, 0x00,                 // Name (ordinal 0).
  50.     0x00, 0x00, 0x00, 0x00,                 // DataVersion
  51.     0x00, 0x00,                             // MemoryFlags
  52.     0x00, 0x00,                             // LanguageId
  53.     0x00, 0x00, 0x00, 0x00,                 // Version
  54.     0x00, 0x00, 0x00, 0x00                  // Characteristics
  55. };
  56.  
  57.  
  58. STATICFN BOOL LoadResFile(HANDLE hfRes, LPTSTR pszFullResFile,
  59.     LPTSTR pszIncludeBuf);
  60. STATICFN BOOL IsValidResFile(PRES pRes, INT cbFileSize);
  61. STATICFN PRES SafeParseResHeader(PRES pRes, INT cbMaxSize);
  62. STATICFN INT SafeNameOrdLen(LPTSTR psz, INT cbMaxLen);
  63. STATICFN VOID SafeDWordAlign(PBYTE *ppb, PINT pcbMax);
  64. STATICFN BOOL WriteDlgIncludeRes(HANDLE hfWrite, LPTSTR pszFullResFile);
  65.  
  66.  
  67.  
  68. /************************************************************************
  69. * OpenResFile
  70. *
  71. * High level function to load the data in a resource file.  The
  72. * function LoadResFile is called to do the actual work, after
  73. * this code does some housekeeping.
  74. *
  75. * Arguments:
  76. *     LPTSTR pszFullPath - The full path to the resource file.
  77. *
  78. * Returns:
  79. *     TRUE if resource file was opened; otherwise, FALSE.
  80. *
  81. ************************************************************************/
  82.  
  83. BOOL OpenResFile(
  84.     LPTSTR pszFullPath)
  85. {
  86.     HCURSOR hcurSave;
  87.     PRESLINK prl;
  88.     PRESLINK prlSave;
  89.     BOOL fSuccess = FALSE;
  90.     INT cDlg;
  91.     HANDLE hfRes;
  92.     TCHAR szInclude[CCHMAXPATH];
  93.     TCHAR szFullInclude[CCHMAXPATH];
  94.     BOOL fIncOpened = FALSE;
  95.  
  96.     hcurSave = SetCursor(hcurWait);
  97.  
  98.     /*
  99.      * Close any existing resource and include file and free memory.
  100.      * It is assumed that if either had been changed, the user was asked
  101.      * if they wanted to save them, because it is too late now.
  102.      */
  103.     FreeRes();
  104.     FreeInclude();
  105.  
  106.     if ((hfRes = CreateFile(pszFullPath, GENERIC_READ,
  107.             FILE_SHARE_READ, NULL, OPEN_EXISTING,
  108.             FILE_FLAG_SEQUENTIAL_SCAN, NULL)) != (HANDLE)-1 &&
  109.             LoadResFile(hfRes, pszFullPath, szInclude)) {
  110.         lstrcpy(szFullResFile, pszFullPath);
  111.         pszResFile = FileInPath(szFullResFile);
  112.  
  113.         ShowFileStatus(TRUE);
  114.  
  115.         /*
  116.          * If there was a DLGINCLUDE resource found, try and open the
  117.          * specified include file, after making sure that it is a
  118.          * fully formed path.
  119.          */
  120.         if (*szInclude) {
  121.             /*
  122.              * Does the include filespec from the res file appear to
  123.              * be a simple filename without a path?  If so, look for
  124.              * it in the same directory that the res file is in.
  125.              * Otherwise, assume it has a fully qualified path.
  126.              */
  127.             if (!HasPath(szInclude)) {
  128.                 lstrcpy(szFullInclude, szFullResFile);
  129.                 lstrcpy(FileInPath(szFullInclude), szInclude);
  130.             }
  131.             else {
  132.                 lstrcpy(szFullInclude, szInclude);
  133.             }
  134.  
  135.             fIncOpened = OpenIncludeFile(szFullInclude);
  136.         }
  137.  
  138.         /*
  139.          * If there wasn't an include resource found, or there was
  140.          * but it couldn't be opened, we want to ask the user for the
  141.          * include file to use for this resource file.
  142.          */
  143.         if (!fIncOpened)
  144.             Open(FILE_INCLUDE);
  145.  
  146.         /*
  147.          * Start counting the dialogs in the resource, but stop at two.
  148.          */
  149.         cDlg = 0;
  150.         for (cDlg = 0, prl = gprlHead; prl; prl = prl->prlNext) {
  151.             if (prl->fDlgResource) {
  152.                 if (++cDlg > 1)
  153.                     break;
  154.  
  155.                 prlSave = prl;
  156.             }
  157.         }
  158.  
  159.         /*
  160.          * If there are multiple dialogs, display the "Select Dialog"
  161.          * dialog to ask the user which one they want to edit.  If
  162.          * there is exactly one dialog, just go ahead and show it
  163.          * initially.
  164.          */
  165.         if (cDlg == 1)
  166.             ResLinkToDialog(prlSave);
  167.         else if (cDlg > 1)
  168.             SelectDialogDialog();
  169.  
  170.         fSuccess = TRUE;
  171.     }
  172.  
  173.     if (hfRes != (HANDLE)-1)
  174.         CloseHandle(hfRes);
  175.  
  176.     ShowFileStatus(TRUE);
  177.     SetCursor(hcurSave);
  178.  
  179.     return fSuccess;
  180. }
  181.  
  182.  
  183.  
  184. /************************************************************************
  185. * LoadResFile
  186. *
  187. * Loads the resource file specified by the passed in file handle.
  188. * This function first verifies that it is a valid resource file.
  189. *
  190. * Arguments:
  191. *   HANDLE hfRes           - File handle to read from.
  192. *   LPTSTR pszFullResFile  - Full name of resource file being loaded.
  193. *   LPTSTR pszIncludeBuf   - Where to return the include file name, if
  194. *                            a DLGINCLUDE resource is found in the res
  195. *                            file.  If not, this buffer gets a null byte
  196. *                            as its first character.
  197. *
  198. * Returns:
  199. *   TRUE if load was successful; otherwise, FALSE is returned.
  200. *
  201. ************************************************************************/
  202.  
  203. STATICFN BOOL LoadResFile(
  204.     HANDLE hfRes,
  205.     LPTSTR pszFullResFile,
  206.     LPTSTR pszIncludeBuf)
  207. {
  208.     HANDLE hAllRes;
  209.     PRES pRes;
  210.     PRES pResAll;
  211.     PRESLINK prl;
  212.     PRESLINK prlT;
  213.     INT cbRead;
  214.     LPTSTR pszResType;
  215.     DWORD cbFileSize;
  216.  
  217.     cbFileSize = GetFileSize((HANDLE)hfRes, NULL);
  218.  
  219.     if (!(hAllRes = GlobalAlloc(GMEM_MOVEABLE, cbFileSize))) {
  220.         Message(MSG_OUTOFMEMORY);
  221.         return FALSE;
  222.     }
  223.  
  224.     *pszIncludeBuf = CHAR_NULL;
  225.     pRes = pResAll = (PRES)GlobalLock(hAllRes);
  226.     if ((cbRead = _lread((HFILE)hfRes, (LPSTR)pResAll, cbFileSize)) != -1 &&
  227.             cbRead == (INT)cbFileSize) {
  228.         if (!IsValidResFile(pResAll, cbFileSize)) {
  229.             Message(MSG_BADRESFILE, pszFullResFile);
  230.         }
  231.         else do {
  232.             pszResType = ResourceType(pRes);
  233.  
  234.             if (IsOrd(pszResType) && OrdID(pszResType) == ORDID_RT_DLGINCLUDE) {
  235.                 /*
  236.                  * Pass back the include file name.  This resource
  237.                  * will not be saved in the res list because it is
  238.                  * going to be explicitly written out later if
  239.                  * necessary.
  240.                  */
  241.                 NameOrdCpy(pszIncludeBuf, (LPTSTR)SkipResHeader(pRes));
  242.             }
  243.             else if (IsOrd(pszResType) &&
  244.                     OrdID(pszResType) == ORDID_RT_RESOURCE32) {
  245.                 /*
  246.                  * This is the dummy resource that identifies a
  247.                  * 32 bit resource file.  This resource should be
  248.                  * skipped also.
  249.                  */
  250.             }
  251.             else {
  252.                 /*
  253.                  * This is some other kind of a resource.
  254.                  * Save it in the resource list.
  255.                  */
  256.                 if (!(prlT = AllocResLink(pRes))) {
  257.                     FreeResList();
  258.                     break;
  259.                 }
  260.  
  261.                 if (!gprlHead) {
  262.                     gprlHead = prl = prlT;
  263.                 }
  264.                 else {
  265.                     prl->prlNext = prlT;
  266.                     prl = prlT;
  267.                 }
  268.             }
  269.  
  270.             /*
  271.              * Move to the next resource.
  272.              */
  273.             pRes = (PRES)((PBYTE)pRes + pRes->HeaderSize + pRes->DataSize);
  274.             DWordAlign((PBYTE *)&pRes);
  275.         } while (pRes < (PRES)((PBYTE)pResAll + cbFileSize));
  276.     }
  277.  
  278.     GlobalUnlock(hAllRes);
  279.     GlobalFree(hAllRes);
  280.  
  281.     return (gprlHead ? TRUE : FALSE);
  282. }
  283.  
  284.  
  285.  
  286. /************************************************************************
  287. * IsValidResFile
  288. *
  289. * This function does some basic checks on the resource file in memory
  290. * pointed to by pbRes.  It does this by walking through the resource
  291. * checking for the resource header info and lengths.
  292. *
  293. * Arguments:
  294. *   PRES pRes      - Pointer to the first resource in the file.
  295. *   INT cbFileSize - Size of the file in memory.
  296. *
  297. * Returns:
  298. *   TRUE if it is a valid resource file, FALSE if not.
  299. *
  300. ************************************************************************/
  301.  
  302. STATICFN BOOL IsValidResFile(
  303.     PRES pRes,
  304.     INT cbFileSize)
  305. {
  306.     INT cbCurLoc = 0;
  307.     PRES pResT;
  308.  
  309.     /*
  310.      * The file is zero size.
  311.      */
  312.     if (!cbFileSize)
  313.         return FALSE;
  314.  
  315.     pResT = pRes;
  316.     while (cbCurLoc < cbFileSize) {
  317.         /*
  318.          * Check this resource for validity.
  319.          */
  320.         if (!(pResT = SafeParseResHeader(pResT, cbFileSize - cbCurLoc)))
  321.             return FALSE;
  322.  
  323.         /*
  324.          * Point just past the resource that was just checked.
  325.          */
  326.         cbCurLoc = (PBYTE)pResT - (PBYTE)pRes;
  327.     }
  328.  
  329.     return (cbCurLoc == cbFileSize) ? TRUE : FALSE;
  330. }
  331.  
  332.  
  333.  
  334. /************************************************************************
  335. * SafeParseResHeader
  336. *
  337. * This function parses the specified resource header and returns a
  338. * pointer to the next resource header in the resource file.  It does
  339. * it in a safe manner, not touching memory beyond the maximum size
  340. * specified.  If the resource header is somehow messed up and
  341. * specifies a size that is larger than will fit in the given maximum
  342. * size, NULL is returned.
  343. *
  344. * Arguments:
  345. *   PRES pRes     - Pointer to the resource.
  346. *   INT cbMaxSize - Maximum size the resource can be.
  347. *
  348. * Returns:
  349. *   A pointer to just past this resource, or NULL if the resource
  350. *   is larger than cbMaxSize.
  351. *
  352. ************************************************************************/
  353.  
  354. STATICFN PRES SafeParseResHeader(
  355.     PRES pRes,
  356.     INT cbMaxSize)
  357. {
  358.     INT cbLen;
  359.     DWORD cbDataSize;
  360.     PBYTE pb;
  361.  
  362.     pb = (PBYTE)pRes;
  363.  
  364.     /*
  365.      * There must be room for the first part of the resource header.
  366.      */
  367.     if (sizeof(RES) > cbMaxSize)
  368.         return FALSE;
  369.  
  370.     pb += sizeof(RES);
  371.     cbMaxSize -= sizeof(RES);
  372.  
  373.     /*
  374.      * Parse the type field then skip over it.
  375.      */
  376.     cbLen = SafeNameOrdLen((LPTSTR)pb, cbMaxSize);
  377.     if (cbLen > cbMaxSize)
  378.         return NULL;
  379.  
  380.     pb += cbLen;
  381.     cbMaxSize -= cbLen;
  382.  
  383.     /*
  384.      * Parse the name field then skip over it.
  385.      */
  386.     cbLen = SafeNameOrdLen((LPTSTR)pb, cbMaxSize);
  387.     if (cbLen > cbMaxSize)
  388.         return NULL;
  389.  
  390.     pb += cbLen;
  391.     cbMaxSize -= cbLen;
  392.  
  393.     SafeDWordAlign(&pb, &cbMaxSize);
  394.  
  395.     /*
  396.      * There must be room for the second part of the resource header.
  397.      */
  398.     if (sizeof(RES2) > cbMaxSize)
  399.         return FALSE;
  400.  
  401.     pb += sizeof(RES2);
  402.     cbMaxSize -= sizeof(RES2);
  403.  
  404.     /*
  405.      * The header size field must be valid.
  406.      */
  407.     if (pRes->HeaderSize != (DWORD)(pb - (PBYTE)pRes))
  408.         return FALSE;
  409.  
  410.     /*
  411.      * Calculate the size of the data, taking into account any
  412.      * padding that may be at the end to make it DWORD aligned.
  413.      */
  414.     cbDataSize = pRes->DataSize;
  415.     DWordAlign((PBYTE *)&cbDataSize);
  416.  
  417.     /*
  418.      * There must be room enough left for the data.
  419.      */
  420.     if (cbDataSize > (DWORD)cbMaxSize)
  421.         return FALSE;
  422.  
  423.     return (PRES)(pb + cbDataSize);
  424. }
  425.  
  426.  
  427.  
  428. /************************************************************************
  429. * SafeNameOrdLen
  430. *
  431. * This function returns the size of the specified name/ordinal.  It
  432. * does it in a safe manner, not touching memory beyond the specified
  433. * maximum size.  If it is a string and the terminating null is not
  434. * found within cbMaxLen bytes, then cbMaxLen plus one is returned.
  435. *
  436. * Arguments:
  437. *   LPTSTR psz   - Pointer to the name/ordinal.
  438. *   INT cbMaxLen - Maximum length to probe.
  439. *
  440. * Returns:
  441. *   The length of the ordinal if it is an ordinal, or the length
  442. *   of the string (plus the null terminator) if it is a string.
  443. *
  444. ************************************************************************/
  445.  
  446. STATICFN INT SafeNameOrdLen(
  447.     LPTSTR psz,
  448.     INT cbMaxLen)
  449. {
  450.     INT cbLen = 0;
  451.  
  452.     if (cbMaxLen == 0)
  453.         return 1;
  454.  
  455.     if (IsOrd(psz))
  456.         return sizeof(ORDINAL);
  457.  
  458.     for (cbLen = 0; cbLen < cbMaxLen && *psz; psz++, cbLen += sizeof(TCHAR))
  459.         ;
  460.  
  461.     /*
  462.      * Account for the null terminator.
  463.      */
  464.     cbLen += sizeof(TCHAR);
  465.  
  466.     return cbLen;
  467. }
  468.  
  469.  
  470.  
  471. /************************************************************************
  472. * SafeDWordAlign
  473. *
  474. * This function aligns the passed pointer to a DWORD boundary.  At the
  475. * same time, it subtracts from the specified counter the amount that
  476. * it had to add to the pointer, if any.
  477. *
  478. * Arguments:
  479. *   PBYTE *ppb  - Points to the pointer to align.
  480. *   PINT pcbMax - Points to the current count to decrement.
  481. *
  482. ************************************************************************/
  483.  
  484. STATICFN VOID SafeDWordAlign(
  485.     PBYTE *ppb,
  486.     PINT pcbMax)
  487. {
  488.     INT cbAlign;
  489.  
  490.     cbAlign = (4 - (((WORD)(DWORD)*ppb) & 3)) % 4;
  491.     *ppb += cbAlign;
  492.     *pcbMax -= cbAlign;
  493. }
  494.  
  495.  
  496.  
  497. /************************************************************************
  498. * WriteRes
  499. *
  500. * Worker routine that does the actual writing out of the resource data.
  501. *
  502. * Arguments:
  503. *   HANDLE hfWrite         - Resource file to write to.
  504. *   LPTSTR pszFullResFile  - Full pathname to the resource file that
  505. *                            is being written.
  506. *
  507. * Returns:
  508. *   TRUE if successful; otherwise, FALSE.
  509. *
  510. ************************************************************************/
  511.  
  512. BOOL WriteRes(
  513.     HANDLE hfWrite,
  514.     LPTSTR pszFullResFile)
  515. {
  516.     PRESLINK prl;
  517.     PRES pRes;
  518.  
  519.     /*
  520.      * Write the special RT_RESOURCE32 dummy resource to the beginning
  521.      * of the resource file.  This resource is aligned, so no padding
  522.      * needs to be done before writing the resource that follows it.
  523.      */
  524.     if (_lwrite((HFILE)hfWrite, abResource32, sizeof(abResource32)) == -1)
  525.         return FALSE;
  526.  
  527.     /*
  528.      * Write out any DLGINCLUDE resource there may be.
  529.      */
  530.     if (!WriteDlgIncludeRes(hfWrite, pszFullResFile))
  531.         return FALSE;
  532.  
  533.     /*
  534.      * Loop through all the resources.
  535.      */
  536.     for (prl = gprlHead; prl; prl = prl->prlNext) {
  537.         if (!(pRes = (PRES)GlobalLock(prl->hRes)))
  538.             return FALSE;
  539.  
  540.         /*
  541.          * Write the actual data.
  542.          */
  543.         if (_lwrite((HFILE)hfWrite, (LPSTR)pRes, prl->cbRes) == -1)
  544.             return FALSE;
  545.  
  546.         /*
  547.          * Write pads out to the next DWORD boundary.
  548.          */
  549.         if (!WriteDWordPad(hfWrite, prl->cbRes))
  550.             return FALSE;
  551.  
  552.         GlobalUnlock(prl->hRes);
  553.     }
  554.  
  555.     return TRUE;
  556. }
  557.  
  558.  
  559.  
  560. /************************************************************************
  561. * WriteDlgIncludeRes
  562. *
  563. * Writes out a DLGINCLUDE resource to the specified resource file for
  564. * the currently open include file.
  565. *
  566. * Arguments:
  567. *   HANDLE hfWrite         - Resource file handle to write to.
  568. *   LPTSTR pszFullResFile  - Full pathname to the resource file that
  569. *                            is being written.
  570. *
  571. * Returns:
  572. *   Number of characters written if the include resource was
  573. *   written successfully (or there wasn't one to write) or -1
  574. *   if an error occurred.
  575. *
  576. ************************************************************************/
  577.  
  578. STATICFN BOOL WriteDlgIncludeRes(
  579.     HANDLE hfWrite,
  580.     LPTSTR pszFullResFile)
  581. {
  582.     INT cbResSize;
  583.     INT cbDataSize;
  584.     PRES pResBegin;
  585.     PBYTE pb;
  586.     INT cbWritten;
  587.     LPTSTR pszInc;
  588.     ORDINAL ordDlgIncName;
  589.     BOOL fSuccess = FALSE;
  590.  
  591.     /*
  592.      * No include file.  Do nothing (return success).
  593.      */
  594.     if (!pszIncludeFile)
  595.         return TRUE;
  596.  
  597.     /*
  598.      * If the include file is in a different directory than the resource
  599.      * file, write the full path to it.  Otherwise, we just write the
  600.      * include file name.
  601.      */
  602.     if (DifferentDirs(pszFullResFile, szFullIncludeFile))
  603.         pszInc = szFullIncludeFile;
  604.     else
  605.         pszInc = pszIncludeFile;
  606.  
  607.     /*
  608.      * The DLGINCLUDE resource name always is the same (a value of 1).
  609.      */
  610.     WriteOrd(&ordDlgIncName, ORDID_DLGINCLUDE_NAME);
  611.  
  612.     /*
  613.      * Determine the size of the resource data.
  614.      */
  615.     cbDataSize = NameOrdLen(pszInc);
  616.  
  617.     /*
  618.      * Determine the resource size.  Note that there is no need for
  619.      * DWORD padding after the res header, because the header will
  620.      * be aligned (there are no strings in it).
  621.      */
  622.     cbResSize = sizeof(RES) +                       // First part of res header.
  623.             sizeof(ORDINAL) +                       // Type ordinal.
  624.             sizeof(ORDINAL) +                       // Name ordinal.
  625.             sizeof(RES2) +                          // Second half of header.
  626.             cbDataSize;                             // Size of data.
  627.  
  628.     if (!(pResBegin = (PRES)MyAlloc(cbResSize)))
  629.         return FALSE;
  630.  
  631.     /*
  632.      * Write the resource header.
  633.      */
  634.     pb = WriteResHeader(pResBegin, cbDataSize, ORDID_RT_DLGINCLUDE,
  635.             (LPTSTR)&ordDlgIncName, MMF_MOVEABLE | MMF_PURE | MMF_DISCARDABLE,
  636.             0, 0, 0, 0);
  637.  
  638.     /*
  639.      * Write the resource data.  This is simply the name
  640.      * of the include file.
  641.      */
  642.     NameOrdCpy((LPTSTR)pb, pszInc);
  643.  
  644.     /*
  645.      * Write the resource to the file.
  646.      */
  647.     cbWritten = _lwrite((HFILE)hfWrite, (LPSTR)pResBegin, cbResSize);
  648.  
  649.     if (cbWritten == cbResSize) {
  650.         /*
  651.          * Write pads out to the next DWORD boundary.
  652.          */
  653.         if (WriteDWordPad(hfWrite, cbWritten))
  654.             fSuccess = TRUE;
  655.     }
  656.  
  657.     MyFree(pResBegin);
  658.  
  659.     return fSuccess;
  660. }
  661.