home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / aniedit / anifile.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-04-17  |  29.6 KB  |  1,111 lines

  1. /****************************************************************************\
  2. *
  3. *     MODULE: anifile.c
  4. *
  5. *     PURPOSE: Processes files for the Animated Cursor Editor
  6. *
  7. *     Copyright 1993-1996 Microsoft Corp.
  8. *
  9. *
  10. * History:
  11. *   21-Apr-1993 JonPa   Wrote it.
  12. *
  13. \****************************************************************************/
  14.  
  15. #include <windows.h>
  16. #include <commdlg.h>
  17. #include "anidefs.h"
  18.  
  19. /****************************************************************************\
  20. *
  21. *     FUNCTION: BOOL CreateFrameFromCursorFile( LPSTR pszFile )
  22. *
  23. *     PURPOSE:  Opens a cursor file, reads the icon info out of it,
  24. *               and creates a frame and step for that icon, then links
  25. *               everything together and updates the listbox.
  26. *
  27. *     NOTES:    This function accesses the global flag gfEditFrame.
  28. *               If this bool is TRUE, then the currently selected frame
  29. *               in the listbox is overwritten.  If it is false, then
  30. *               a new frame is created and inserted after the currently
  31. *               selected frame (or at the end if no selection).
  32. *
  33. * History:
  34. *   21-Apr-1993 JonPa   Created it
  35. *
  36. \****************************************************************************/
  37. BOOL CreateFrameFromCursorFile(HWND hwnd,  LPTSTR pszFile, BOOL fEdit) {
  38.     PFRAME pf;
  39.     HANDLE hf;
  40.     PSTEP psOld, psNew;
  41.     DWORD ckSize;
  42.     int iSel;
  43.     int cSel;
  44.  
  45.     cSel = GetSelStepCount(hwnd);
  46.  
  47.     if ( (fEdit && (cSel != 1)) || cSel > 1) {
  48.         FmtMessageBox( hwnd, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP,
  49.                 TRUE, fEdit ? MSG_MUSTEQONEFAME : MSG_LESSEQONEFRAME);
  50.         return FALSE;
  51.     }
  52.  
  53.     /* get currently selected frame */
  54.     GetCurrentSel(hwnd, DLG_MAIN_FRAMELIST, &iSel, 1, &cSel );
  55.  
  56.     if (cSel == 0)
  57.         psOld = NULL;
  58.     else
  59.         psOld = GetStep(hwnd, iSel);
  60.  
  61.     /*
  62.      * If not editing, create a new step
  63.      */
  64.     if (!fEdit || !IsValidPS(psOld)) {
  65.         psNew = NewStep();
  66.  
  67.         if (psNew == NULL) {
  68.             return FALSE;
  69.         }
  70.     } else {
  71.         psNew = NULL;
  72.     }
  73.  
  74.     hf = CreateFile(pszFile, GENERIC_READ,
  75.              0, NULL,
  76.              OPEN_EXISTING,
  77.              FILE_ATTRIBUTE_NORMAL,
  78.              NULL);
  79.  
  80.     if (hf == INVALID_HANDLE_VALUE)
  81.         return FALSE;
  82.  
  83.     ckSize = GetFileSize(hf, NULL);
  84.  
  85.     /* get the frame out of the file */
  86.     pf = ReadIconFromFile(hwnd, hf, ckSize);
  87.     CloseHandle(hf);
  88.  
  89.     if (pf == NULL) {
  90.         if (psNew != NULL)
  91.             FreeMem(psNew);
  92.         return FALSE;
  93.     }
  94.  
  95.     if (psNew != NULL) {
  96.  
  97.         if (IsValidPS(psOld)) {
  98.             psNew->jif = psOld->jif;
  99.             iSel += 1;
  100.         } else {
  101.             psNew->jif = ganiAcon.anih.jifRate;
  102.             iSel = SendDlgItemMessage(hwnd, DLG_MAIN_FRAMELIST, LB_GETCOUNT,
  103.                     0, 0);
  104.         }
  105.  
  106.         LinkStepFrame(psNew, pf);
  107.  
  108.         SendDlgItemMessage(hwnd, DLG_MAIN_FRAMELIST, LB_INSERTSTRING, iSel,
  109.             (LPARAM)psNew);
  110.  
  111.         SetCurrentSel(hwnd, DLG_MAIN_FRAMELIST, FALSE, iSel);
  112.  
  113.     } else {
  114.         HWND hwndLB = GetDlgItem(hwnd, DLG_MAIN_FRAMELIST);
  115.  
  116.         /*
  117.          * Delete the old frame and point the step to the new one.
  118.          */
  119.         LinkStepFrame(psOld, pf);
  120.  
  121.         InvalidateRect(hwndLB, NULL, TRUE);
  122.     }
  123.  
  124.     return TRUE;
  125. }
  126.  
  127.  
  128. /****************************************************************************\
  129. *
  130. *     FUNCTION: HICON ConvertDataToIcon( PFRAME pf )
  131. *
  132. *     PURPOSE:
  133. *
  134. *
  135. *
  136. *
  137. * History:
  138. *   23-Apr-1993 JonPa   copied from Win NT USERs ReadIconGuts
  139. *
  140. \****************************************************************************/
  141. HICON ConvertDataToIcon( PFRAME pf, WORD *pxHotSave, WORD *pyHotSave )
  142. {
  143.     NEWHEADER *pnh;
  144.     NEWHEADER *pnhBase;
  145.     RESDIR *prd;
  146.     int offMatch;
  147.     ICONFILERESDIR *pird;
  148.     PCURSORRESOURCE pcres;
  149.     BOOL fIcon;
  150.     HICON hicon;
  151.     WORD x, y;
  152.     LPBYTE pbBits;
  153.  
  154.     pnhBase = (NEWHEADER *)pf->abIcon;
  155.  
  156.     /*
  157.      * Construct a fake array of RESDIR entries using the info at the head
  158.      * of the file.  Store the data offset in the idIcon WORD so it can be
  159.      * returned by RtlGetIdFromDirectory.
  160.      */
  161.     pnh = (NEWHEADER *)LocalAlloc(LMEM_FIXED, sizeof(NEWHEADER) +
  162.             (pnhBase->cResources * sizeof(RESDIR)));
  163.     if (pnh == NULL)
  164.         return NULL;
  165.  
  166.     *pnh = *pnhBase;
  167.     prd = (RESDIR *)(pnh + 1);
  168.     pird = (ICONFILERESDIR *)(pnhBase + 1);
  169.  
  170.     /* prime pird for first line of loop */
  171.     pird--;
  172.  
  173.     for (offMatch = 0; offMatch < (int)pnh->cResources; offMatch++, prd++) {
  174.  
  175.         /*
  176.          * Get the next resource directory from the icon file.
  177.          */
  178.  
  179.         ++pird;
  180.  
  181.         /*
  182.          * Convert from the icon editor's resource directory format
  183.          * to the post-RC.EXE format LookupIconIdFromDirectory expects.
  184.          */
  185.         if (pnh->rt == 1) {     // ICON
  186.             prd->ResInfo.Icon.Width = pird->bWidth;
  187.             prd->ResInfo.Icon.Height = pird->bHeight;
  188.             prd->ResInfo.Icon.ColorCount = pird->bColorCount;
  189.             prd->ResInfo.Icon.reserved = 0;
  190.         } else {                // CURSOR
  191.             prd->ResInfo.Cursor.Width = pird->bWidth;
  192.             prd->ResInfo.Cursor.Height = pird->bHeight;
  193.         }
  194.         prd->Planes = 0;                // Hopefully nobody uses this
  195.         prd->BitCount = 0;              //        "        "
  196.         prd->BytesInRes = pird->dwDIBSize;
  197.         prd->idIcon = (WORD)pird->dwDIBOffset;
  198.     }
  199.  
  200.     /*
  201.      * NOTE: nh.rt is NOT an RT_ type value.  For instance, nh.rt == 1 for
  202.      * an icon file where as 1 == RT_CURSOR, not RT_ICON.
  203.      */
  204.  
  205.     fIcon = (pnhBase->rt == 1);
  206.     offMatch = LookupIconIdFromDirectory((PBYTE)pnh, fIcon);
  207.  
  208.     LocalFree(pnh);
  209.  
  210.     if (fIcon) {
  211.         pcres = (PCURSORRESOURCE)&(pf->abIcon[offMatch]);
  212.         *pxHotSave = gcxCursor / 2;
  213.         *pyHotSave = gcyCursor / 2;
  214.     } else {
  215.  
  216.         offMatch -= (sizeof(pcres->xHotspot) + sizeof(pcres->yHotspot));
  217.  
  218.         for(; pird->dwDIBOffset != (WORD)offMatch &&
  219.                 pird != (ICONFILERESDIR *)(pnhBase + 1); pird--);
  220.  
  221.         pcres = (PCURSORRESOURCE)&(pf->abIcon[offMatch]);
  222.  
  223.         x = pcres->xHotspot;
  224.         y = pcres->yHotspot;
  225.         *pxHotSave = pcres->xHotspot = pird->xHotspot;
  226.         *pyHotSave = pcres->yHotspot = pird->yHotspot;
  227.     }
  228.  
  229.  
  230.     // Buffer must be aligned
  231.     pbBits = LocalAlloc(LMEM_FIXED, pf->rtag.ckSize - offMatch );
  232.     if (pbBits) {
  233.         CopyMemory( pbBits, pcres, pf->rtag.ckSize - offMatch );
  234.  
  235.         hicon = CreateIconFromResource( pbBits,
  236.                 pf->rtag.ckSize - offMatch, fIcon, 0x00030000);
  237.  
  238.         LocalFree( pbBits );
  239.     } else
  240.         hicon = NULL;
  241.  
  242.     if(!fIcon) {
  243.         pcres->xHotspot = x;
  244.         pcres->yHotspot = y;
  245.     }
  246.  
  247.     return hicon;
  248. }
  249.  
  250.  
  251.  
  252.  
  253. /****************************************************************************\
  254. *
  255. *     FUNCTION: PFRAME ReadIconFromFile(HWND hwnd, HANDLE hf, DWORD ckSize)
  256. *
  257. *     PURPOSE:  Reads the icon info out of a file,
  258. *               and creates a frame for that icon.
  259. *
  260. *
  261. * History:
  262. *   22-Apr-1993 JonPa   Created it
  263. *
  264. \****************************************************************************/
  265. PFRAME ReadIconFromFile(HWND hwnd, HANDLE hf, DWORD ckSize) {
  266.     PFRAME pf = AllocMem( sizeof( FRAME ) + ckSize );
  267.     DWORD cbRead;
  268.     PFRAME pfList;
  269.  
  270.     if (pf != NULL) {
  271.         pf->cRef = 0;
  272.  
  273.         if (ReadFile(hf, pf->abIcon, ckSize, &cbRead, NULL) &&
  274.                 cbRead == ckSize) {
  275.             /* got the data, now set up the rest of the frame and link it in */
  276.             pf->dwCheckSum = CalcCheckSum( pf->abIcon, ckSize );
  277.             pf->rtag.ckID = FOURCC_icon;
  278.             pf->rtag.ckSize = ckSize;
  279.  
  280.             /* Check if this fram is already in the list */
  281.             for (pfList = gpfrmFrames; pfList != NULL;
  282.                     pfList = pfList->pfrmNext ) {
  283.                 if (pf->dwCheckSum == pfList->dwCheckSum &&
  284.                         pf->rtag.ckSize == pfList->rtag.ckSize &&
  285.                         memcmp( pf->abIcon, pfList->abIcon, ckSize ) == 0) {
  286.                     /*
  287.                      * These frames are the same, coalesce them into a
  288.                      * sequence.
  289.                      */
  290.                     FreeMem(pf);
  291.                     pf = pfList;
  292.                     break;
  293.                 }
  294.             }
  295.  
  296.             if (pfList == NULL) {
  297.                 /*
  298.                  * Did not find a dup, create an icon for this frame
  299.                  */
  300.                 pf->hcur = ConvertDataToIcon( pf, &(pf->xHotSpot),
  301.                         &(pf->yHotSpot) );
  302.  
  303.                 pf->pfrmNext = gpfrmFrames;
  304.                 gpfrmFrames = pf;
  305.             }
  306.  
  307.         } else {
  308.             /* File Error */
  309.             FreeMem(pf);
  310.             pf = NULL;
  311.         }
  312.  
  313.     }
  314.  
  315.     return pf;
  316. }
  317.  
  318. /****************************************************************************\
  319. *
  320. *     FUNCTION: HANDLE PromptAndOpenFile( )
  321. *
  322. *     PURPOSE:  Pust up the standard open dialog and then opens the file
  323. *
  324. *
  325. *
  326. *
  327. * History:
  328. *   21-Apr-1993 JonPa   Created it
  329. *
  330. \****************************************************************************/
  331. HANDLE PromptAndOpenFile(
  332.     HWND hwnd,
  333.     DWORD  cchFileTitle,
  334.     LPTSTR pszFileTitle,
  335.     DWORD  cchFileName,
  336.     LPTSTR pszFileName,
  337.     LPTSTR pszFilter
  338.     )
  339. {
  340.     HANDLE hf = INVALID_HANDLE_VALUE;
  341.  
  342.     if (PromptForFile( hwnd, cchFileTitle, pszFileTitle, cchFileName,
  343.                 pszFileName, pszFilter, NULL, FALSE )) {
  344.  
  345.         /* Open the file. */
  346.  
  347.         hf = CreateFile(pszFileName, GENERIC_READ,
  348.                 0, NULL,
  349.                 OPEN_EXISTING,
  350.                 FILE_ATTRIBUTE_NORMAL,
  351.                 NULL);
  352.  
  353.         if (hf == INVALID_HANDLE_VALUE) {
  354.             FmtMessageBox( hwnd, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP, TRUE,
  355.                 MSG_CANTOPENFILE, pszFileName );
  356.         }
  357.     }
  358.  
  359.     return hf;
  360. }
  361.  
  362.  
  363. /****************************************************************************\
  364. *
  365. *     FUNCTION: HANDLE PromptForFile( )
  366. *
  367. *     PURPOSE:  Pust up the standard open dialog
  368. *
  369. *
  370. *
  371. *
  372. * History:
  373. *   28-Apr-1993 JonPa   Created it from PromptAndOpenFile
  374. *
  375. \****************************************************************************/
  376. BOOL PromptForFile(
  377.     HWND hwnd,
  378.     DWORD  cchFileTitle,
  379.     LPTSTR pszFileTitle,
  380.     DWORD  cchFileName,
  381.     LPTSTR pszFile,
  382.     LPTSTR pszFilter,
  383.     LPTSTR pszDlgTitle,
  384.     BOOL fSave
  385.     )
  386. {
  387.     OPENFILENAME ofn;
  388.  
  389.     ZeroMemory(&ofn, sizeof(ofn));
  390.  
  391.     /* Set the members of the OPENFILENAME structure. */
  392.  
  393.     ofn.lStructSize = sizeof(OPENFILENAME);
  394.     ofn.hwndOwner = hwnd;
  395.  
  396.     ofn.lpstrFilter = pszFilter;
  397.     ofn.nFilterIndex = 0;
  398.  
  399.     ofn.lpstrFile = pszFile;
  400.     ofn.nMaxFile = cchFileName;
  401.  
  402.     ofn.lpstrFileTitle = pszFileTitle;
  403.     ofn.nMaxFileTitle = cchFileTitle;
  404.  
  405.     ofn.lpstrTitle = pszDlgTitle;
  406.  
  407.     ofn.lpstrDefExt = gpszANI;
  408.  
  409.     if (fSave) {
  410.         ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
  411.     } else {
  412.         ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
  413.     }
  414.  
  415.     /* Display the SaveAs or Open dialog box. */
  416.  
  417.     return fSave ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
  418.  
  419. }
  420.  
  421. /****************************************************************************\
  422. *
  423. *     FUNCTION: BOOL ReadAniFile( HWND hwnd, HANDLE hf ) {
  424. *
  425. *     PURPOSE:
  426. &
  427. *   Loads an animatied cursor from a RIFF file.  The RIFF file format for
  428. *   animated cursors looks like this:
  429. *
  430. *   RIFF( 'ACON'
  431. *       LIST( 'INFO'
  432. *           INAM( <name> )
  433. *           IART( <artist> )
  434. *       )
  435. *       anih( <anihdr> )
  436. *       [rate( <rateinfo> )  ]
  437. *       ['seq '( <seq_info> )]
  438. *       LIST( 'fram' icon( <icon_file> ) )
  439. *   )
  440. *
  441. *
  442. * History:
  443. *   02-Oct-1991 DarrinM     Created. (in Win32 user)
  444. *   17-Mar-1993 JonPa       Rewrote to use RIFF format instead of RAD
  445. *   21-Apr-1993 JonPa       Copied it to anifile.c and tweeked it.
  446. *
  447. \****************************************************************************/
  448. BOOL ReadAniFile( HWND hwnd, HANDLE hf ) {
  449.  
  450.     RTAG tag;
  451.     DWORD cbRead;
  452.     BOOL fSuccess = FALSE;
  453.     JIF *pjifRate = NULL;
  454.     DWORD *pseq = NULL;
  455.     PFRAME *ppfram = NULL;
  456.     int iFrame = 0;
  457.     int i;
  458.  
  459.     if (!ReadTag(hf, &tag))
  460.         goto laiFileErr;
  461.  
  462.     /*
  463.      * Make sure it's a RIFF ANI file
  464.      */
  465.     if (tag.ckID != FOURCC_RIFF)
  466.         goto laiFileErr;
  467.  
  468.     /* read the chunk type */
  469.     if(!ReadFile(hf, &tag.ckID, sizeof(tag.ckID), &cbRead, NULL) ||
  470.             cbRead < sizeof(tag.ckID)) {
  471.         goto laiFileErr;
  472.     }
  473.  
  474.     if (tag.ckID != FOURCC_ACON)
  475.         goto laiFileErr;
  476.  
  477.     /* look for 'anih', 'rate', 'seq ', and 'icon' chunks */
  478.     while( ReadTag(hf, &tag)) {
  479.  
  480.         switch( tag.ckID ) {
  481.         case FOURCC_anih:
  482.             if (!ReadChunk(hf, &tag, &ganiAcon.anih))
  483.                 goto laiFileErr;
  484.  
  485.             if (!(ganiAcon.anih.fl & AF_ICON) || (ganiAcon.anih.cFrames == 0))
  486.                 goto laiFileErr;
  487.  
  488.             /*
  489.              * Allocate space for the ANIHEADER, and a seq and
  490.              * rate table (in case we run into one later).
  491.              */
  492.  
  493.             pjifRate = AllocMem( ganiAcon.anih.cSteps * sizeof(JIF) +
  494.                     ganiAcon.anih.cSteps * sizeof(DWORD) +
  495.                     ganiAcon.anih.cSteps * sizeof(PFRAME));
  496.  
  497.  
  498.             if (pjifRate == NULL)
  499.                 goto laiFileErr;
  500.  
  501.             pseq = (DWORD *)(pjifRate + ganiAcon.anih.cSteps);
  502.             ppfram = (PFRAME *)(pseq + ganiAcon.anih.cSteps);
  503.  
  504.             for( i = 0; i < (int)ganiAcon.anih.cSteps; i++ ) {
  505.                 pjifRate[i] = ganiAcon.anih.jifRate;
  506.                 pseq[i] = i;
  507.                 ppfram[i] = NULL;
  508.             }
  509.             break;
  510.  
  511.  
  512.         case FOURCC_rate:
  513.             /*
  514.              * If we find a rate chunk, read it into its preallocated
  515.              * space.
  516.              */
  517.             if(!ReadChunk(hf, &tag, (PBYTE)pjifRate))
  518.                 goto laiFileErr;
  519.             break;
  520.  
  521.  
  522.         case FOURCC_seq:
  523.             /*
  524.              * If we find a seq chunk, read it into its preallocated
  525.              * space.
  526.              */
  527.             if(!ReadChunk(hf, &tag, (PBYTE)pseq))
  528.                 goto laiFileErr;
  529.             break;
  530.  
  531.  
  532.         case FOURCC_LIST: {
  533.             DWORD cbChunk = PADUP(tag.ckSize);
  534.  
  535.             /*
  536.              * See if this list is the 'fram' list of icon chunks
  537.              */
  538.             if(!ReadFile(hf, &tag.ckID, sizeof(tag.ckID), &cbRead, NULL) ||
  539.                     cbRead < sizeof(tag.ckID)) {
  540.                 goto laiFileErr;
  541.             }
  542.  
  543.             cbChunk -= cbRead;
  544.  
  545.             if (tag.ckID == FOURCC_fram) {
  546.  
  547.                 while(cbChunk >= sizeof(tag)) {
  548.                     if (!ReadTag(hf, &tag))
  549.                         goto laiFileErr;
  550.  
  551.                     cbChunk -= sizeof(tag);
  552.  
  553.                     if(tag.ckID == FOURCC_icon) {
  554.                         PFRAME pfrm;
  555.  
  556.                         /*
  557.                          * Ok, load the icon/cursor bits,
  558.                          */
  559.                         pfrm = ReadIconFromFile(hwnd, hf, tag.ckSize);
  560.  
  561.                         if (pfrm == NULL) {
  562.                             goto laiFileErr;
  563.                         }
  564.  
  565.                         for( i = 0; i < (int)ganiAcon.anih.cSteps; i++ ) {
  566.                             if (pseq[i] == (DWORD)iFrame) {
  567.                                 ppfram[i] = pfrm;
  568.                             }
  569.                         }
  570.  
  571.                         iFrame++;
  572.  
  573.                     } else {
  574.                         /*
  575.                          * Unknown chunk in fram list, just ignore it
  576.                          */
  577.                         SkipChunk(hf, &tag);
  578.                     }
  579.  
  580.                     cbChunk -= PADUP(tag.ckSize);
  581.                 }
  582.             } else if (tag.ckID == FOURCC_INFO) {
  583.                 /* now look for INAM and IART chunks */
  584.  
  585.                 while( cbChunk >= sizeof(tag) ) {
  586.  
  587.                     if (!ReadTag(hf, &tag))
  588.                         goto laiFileErr;
  589.  
  590.                     cbChunk -= sizeof(tag);
  591.  
  592.                     switch( tag.ckID ) {
  593.                     case FOURCC_INAM:
  594.                         if (cbChunk < tag.ckSize ||
  595.                                 !ReadChunkN(hf, &tag, ganiAcon.azTitle,
  596.                                                 sizeof(ganiAcon.azTitle)))
  597.                             goto laiFileErr;
  598.  
  599.                         cbChunk -= PADUP(tag.ckSize);
  600.                         break;
  601.  
  602.                     case FOURCC_IART:
  603.                         if (cbChunk < tag.ckSize ||
  604.                                 !ReadChunkN(hf, &tag, ganiAcon.azCreator,
  605.                                         sizeof(ganiAcon.azCreator)))
  606.                             goto laiFileErr;
  607.  
  608.                         cbChunk -= PADUP(tag.ckSize);
  609.                         break;
  610.  
  611.                     default:
  612.                         if (!SkipChunk( hf, &tag ))
  613.                             goto laiFileErr;
  614.  
  615.                         cbChunk -= PADUP(tag.ckSize);
  616.                         break;
  617.                     }
  618.                 }
  619.  
  620.             } else {
  621.                 /*
  622.                  * Not the fram list or the INFO list.  Skip
  623.                  * the rest of this chunk.  (Don't forget that we have
  624.                  * already skipped one dword!)
  625.                  */
  626.                 tag.ckSize = cbChunk;
  627.                 SkipChunk(hf, &tag);
  628.                 break;
  629.             }
  630.  
  631.             break;
  632.         }
  633.  
  634.  
  635.  
  636.         default:
  637.             /*
  638.              * We're not interested in this chunk, skip it.
  639.              */
  640.             if(!SkipChunk(hf, &tag))
  641.                 goto laiFileErr;
  642.             break;
  643.  
  644.         }
  645.  
  646.     }
  647.  
  648.     /*
  649.      * Update the frame count incase we coalesced some frames while reading
  650.      * in the file.
  651.      */
  652.     ganiAcon.anih.cFrames = iFrame;
  653.  
  654.     /*
  655.      * Now build up the listbox
  656.      */
  657.  
  658.     for( i = 0; i < (int)ganiAcon.anih.cSteps; i++ ) {
  659.         PSTEP ps;
  660.  
  661.         ps = NewStep();
  662.         if (ps == NULL)
  663.             goto laiFileErr;
  664.  
  665.         ps->jif = pjifRate[i];
  666.         LinkStepFrame(ps, ppfram[i]);
  667.  
  668.         SendDlgItemMessage(hwnd, DLG_MAIN_FRAMELIST, LB_INSERTSTRING, i,
  669.                 (LPARAM)ps);
  670.     }
  671.  
  672.     SetDlgItemText(hwnd, DLG_MAIN_TITLE, ganiAcon.azTitle);
  673.     SetDlgItemText(hwnd, DLG_MAIN_AUTHOR, ganiAcon.azCreator);
  674.  
  675.     SendDlgItemMessage(hwnd, DLG_MAIN_PREVIEW, PM_NEWCURSOR, 0, 0);
  676.     fSuccess = TRUE;
  677.  
  678. laiFileErr:
  679.  
  680.     if (pjifRate != NULL)
  681.         FreeMem(pjifRate);
  682.  
  683.     if (!fSuccess)
  684.         NewAniCursor(hwnd);
  685.  
  686.     CloseHandle(hf);
  687.  
  688.     return fSuccess;
  689. }
  690.  
  691.  
  692.  
  693. /***************************************************************************\
  694. * DWORD CalcCheckSum( PBYTE pb );
  695. *
  696. *
  697. * History:
  698. *
  699. * 23-Apr-1993 JonPa     Created.
  700. \***************************************************************************/
  701. DWORD CalcCheckSum( PBYTE pb, DWORD cb ) {
  702.     DWORD dw = 0;
  703.  
  704.     while(cb--)
  705.         dw += (DWORD)*pb++;
  706.  
  707.     return dw;
  708. }
  709.  
  710. /***************************************************************************\
  711. * ReadTag, ReadChunk, SkipChunk
  712. *
  713. * Some handy functions for reading RIFF files.
  714. *
  715. * History:
  716. * 10-02-91 DarrinM      Created.
  717. * 03-25-93 Jonpa        Changed to use RIFF format instead of ASDF
  718. * 23-Apr-1993 JonPa     Copied from Win NT USER.
  719. \***************************************************************************/
  720. BOOL ReadTag(
  721.     HANDLE hf,
  722.     PRTAG ptag)
  723. {
  724.     DWORD cbActual;
  725.  
  726.     ptag->ckID = ptag->ckSize = 0L;
  727.  
  728.     if (!ReadFile(hf, ptag, sizeof(RTAG), &cbActual, NULL) ||
  729.             (cbActual != sizeof(RTAG)))
  730.         return FALSE;
  731.  
  732.     /* no need to align file pointer since RTAG is already word aligned */
  733.     return TRUE;
  734. }
  735.  
  736.  
  737. BOOL ReadChunk(
  738.     HANDLE hf,
  739.     PRTAG ptag,
  740.     PVOID pv)
  741. {
  742.     DWORD cbActual;
  743.  
  744.     if (!ReadFile(hf, pv, ptag->ckSize, &cbActual, NULL) ||
  745.             (cbActual != ptag->ckSize))
  746.         return FALSE;
  747.  
  748.     /* WORD align file pointer */
  749.     if( ptag->ckSize & 1 )
  750.         SetFilePointer(hf, 1, NULL, FILE_CURRENT);
  751.  
  752.     return TRUE;
  753. }
  754.  
  755.  
  756. BOOL ReadChunkN(
  757.     HANDLE hf,
  758.     PRTAG ptag,
  759.     PVOID pv,
  760.     DWORD cbMax)
  761. {
  762.     DWORD cbActual;
  763.     DWORD cbRead = min( cbMax, ptag->ckSize );
  764.  
  765.     if (!ReadFile(hf, pv, ptag->ckSize, &cbActual, NULL) ||
  766.             (cbActual != cbRead))
  767.         return FALSE;
  768.  
  769.     /* WORD align file pointer */
  770.  
  771.     cbRead = ptag->ckSize - cbActual;
  772.  
  773.     if( ptag->ckSize & 1 )
  774.         cbRead++;
  775.  
  776.     return SetFilePointer(hf, cbRead, NULL, FILE_CURRENT) != 0xFFFFFFFF;
  777. }
  778.  
  779. BOOL SkipChunk(
  780.     HANDLE hf,
  781.     PRTAG ptag)
  782. {
  783.     /* Round ptag->ckSize up to nearest word boundary to maintain alignment */
  784.     return SetFilePointer(hf, PADUP(ptag->ckSize), NULL, FILE_CURRENT) !=
  785.             0xFFFFFFFFL;
  786. }
  787.  
  788. /****************************************************************************\
  789. *
  790. *     FUNCTION: VOID GetTempCursorFileName( szFileName );
  791. *
  792. *     PURPOSE:  Create a temporary .cur filename
  793. *
  794. *
  795. * History:
  796. *   22-Apr-1993 JonPa   Created it
  797. *
  798. \****************************************************************************/
  799. BOOL GetTempCursorFileName( LPTSTR pszName ) {
  800.     TCHAR szPath[MAX_PATH];
  801.  
  802.     if( GetTempPath( MAX_PATH, szPath ) >= MAX_PATH )
  803.         lstrcpy( pszName, TEXT(".") );
  804.  
  805.  
  806.     return GetTempFileName(szPath, TEXT("ae"), 0, pszName) != 0;
  807. }
  808.  
  809. /****************************************************************************\
  810. *
  811. *     FUNCTION: BOOL SaveAniFile( HWND hwnd, HANDLE hf )
  812. *
  813. *     PURPOSE:
  814. &
  815. *   Saves an animatied cursor to a RIFF file.  The RIFF file format for
  816. *   animated cursors looks like this:
  817. *
  818. *   RIFF( 'ACON'
  819. *       [LIST( 'INFO'
  820. *           [INAM( <name> )]
  821. *           [IART( <artist> )]
  822. *       )]
  823. *       anih( <anihdr> )
  824. *       [rate( <rateinfo> )  ]
  825. *       ['seq '( <seq_info> )]
  826. *       LIST( 'fram' icon( <icon_file> ) )
  827. *   )
  828. *
  829. *
  830. * History:
  831. *   29-Apr-1993 JonPa   Created it.
  832. *
  833. \****************************************************************************/
  834. BOOL SaveAniFile( HWND hwnd, HANDLE hf ) {
  835.     int cSteps, i;
  836.     int cFrames;
  837.     PFRAME pf;
  838.     DWORD cbFile, cbFram, cbINFO, cbTitle, cbAuthor;
  839.     BOOL fRate, fSeq;
  840.     RTAG rtag;
  841.     PJIF pjif;
  842.     DWORD *pseq;
  843.     PFRAME *pfrm;
  844.  
  845.     fRate = fSeq = FALSE;
  846.     cbINFO = cbFram = cbFile = cbTitle = cbAuthor = 0;
  847.  
  848.     PausePreview(hwnd, DLG_MAIN_PREVIEW);
  849.  
  850.     cSteps = GetStepCount(hwnd);
  851.  
  852.     if( cSteps == LB_ERR ) {
  853.         FmtMessageBox( ghwndMain, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP,
  854.                 TRUE, MSG_OUTOFRESOUCES );
  855.         return FALSE;
  856.     }
  857.  
  858.     cFrames = 0;
  859.     for( pf = gpfrmFrames; pf != NULL; pf = pf->pfrmNext ) {
  860.         pf->iFrame = -1;
  861.         cFrames++;
  862.     }
  863.  
  864.     ganiAcon.anih.cSteps = cSteps;
  865.  
  866.     pjif = AllocMem( (sizeof(JIF) + sizeof(DWORD) + sizeof(PFRAME)) * cSteps );
  867.  
  868.     if(pjif == NULL)
  869.         return FALSE;
  870.  
  871.     pseq = (DWORD *)&pjif[cSteps];
  872.     pfrm = (PFRAME *)&pseq[cSteps];
  873.  
  874.     cFrames = 0;
  875.  
  876.     for( i = 0; i < cSteps; i++ ) {
  877.         PSTEP ps;
  878.  
  879.         ps = GetStep(hwnd, i);
  880.  
  881.         if( IsValidPS(ps) ) {
  882.  
  883.             if (ps->pfrmFrame->iFrame == -1) {
  884.  
  885.                 cbFram += sizeof(RTAG);
  886.                 cbFram += PADUP(ps->pfrmFrame->rtag.ckSize);
  887.  
  888.                 ps->pfrmFrame->iFrame = cFrames;
  889.                 pfrm[cFrames++] = ps->pfrmFrame;
  890.  
  891.             } else
  892.                 fSeq = TRUE;
  893.  
  894.             pseq[i] = ps->pfrmFrame->iFrame;
  895.  
  896.             if ((pjif[i] = ps->jif) != ganiAcon.anih.jifRate) {
  897.                 fRate = TRUE;
  898.             }
  899.         }
  900.     }
  901.  
  902.     ganiAcon.anih.cbSizeof = sizeof(ganiAcon.anih);
  903.     ganiAcon.anih.cFrames = cFrames;
  904.     ganiAcon.anih.fl = AF_ICON | (fSeq ? AF_SEQUENCE : 0);
  905.  
  906.     cbTitle = GetDlgItemTextA(hwnd, DLG_MAIN_TITLE, ganiAcon.azTitle,
  907.                             COUNTOF(ganiAcon.azTitle));
  908.  
  909.     cbAuthor = GetDlgItemTextA(hwnd, DLG_MAIN_AUTHOR, ganiAcon.azCreator,
  910.                             COUNTOF(ganiAcon.azCreator));
  911.  
  912.     /*
  913.      * At this point, cbFram == the size required by all the frames,
  914.      * add in the rate, seq, anih, and INFO list sizes as well as
  915.      * all the other overhead.
  916.      */
  917.  
  918.     cbFram += sizeof(FOURCC);     //fram type
  919.  
  920.     cbFile = cbFram;
  921.  
  922.     cbFile += sizeof(FOURCC) +    //ACON type
  923.                     sizeof(RTAG) +      //anih tag
  924.                         PADUP(sizeof(ANIHEADER)) +
  925.                     sizeof(RTAG);       //LIST tag (for fram list)
  926.  
  927.  
  928.     if( cbTitle || cbAuthor) {
  929.         /*
  930.          * Remember, azCreator, and azTitle are ANSI strings!
  931.          */
  932.         if( cbTitle ) {
  933.             cbTitle += 1; //add in ASCIIZ terminator
  934.             cbINFO +=   sizeof(RTAG) +     //INAM tag
  935.                         PADUP( cbTitle * sizeof(char));
  936.         }
  937.  
  938.         if (cbAuthor) {
  939.             cbAuthor += 1; //add in ASCIIZ terminator
  940.             cbINFO +=   sizeof(RTAG) +     //IART tag
  941.                         PADUP(cbAuthor * sizeof(char));
  942.         }
  943.  
  944.         cbINFO +=  sizeof(FOURCC);      //INFO type
  945.  
  946.         cbFile +=   sizeof(RTAG) +      //LIST tag
  947.                     cbINFO;
  948.     }
  949.  
  950.  
  951.     if (fSeq) {
  952.         cbFile += sizeof(RTAG) +    //seq tag
  953.                     PADUP(cSteps * sizeof(DWORD));
  954.     }
  955.  
  956.     if (fRate) {
  957.         cbFile += sizeof(RTAG) +    //rate tag
  958.                     PADUP(cSteps * sizeof(JIF));
  959.     }
  960.  
  961.     /*
  962.      * Now we have all the structures built in memory, it's time to
  963.      * write them out in RIFF ACON format!
  964.      */
  965.     rtag.ckID = FOURCC_RIFF;
  966.     rtag.ckSize = cbFile;
  967.  
  968.     RET_CLOSE_IF_ERR( WriteTag(hf, &rtag), hf );
  969.  
  970.     RET_CLOSE_IF_ERR( WriteType(hf, FOURCC_ACON), hf );
  971.  
  972.     if( cbTitle || cbAuthor) {
  973.         rtag.ckID = FOURCC_LIST;
  974.         rtag.ckSize = cbINFO;
  975.  
  976.         RET_CLOSE_IF_ERR( WriteTag(hf, &rtag), hf );
  977.  
  978.         RET_CLOSE_IF_ERR( WriteType(hf, FOURCC_INFO), hf );
  979.  
  980.         if (cbTitle) {
  981.             rtag.ckID = FOURCC_INAM;
  982.             rtag.ckSize = cbTitle;
  983.             RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, ganiAcon.azTitle), hf );
  984.         }
  985.  
  986.         if (cbAuthor) {
  987.             rtag.ckID = FOURCC_IART;
  988.             rtag.ckSize = cbAuthor;
  989.             RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, ganiAcon.azCreator), hf );
  990.         }
  991.     }
  992.  
  993.     /* write anih */
  994.     rtag.ckID = FOURCC_anih;
  995.     rtag.ckSize = sizeof(ganiAcon.anih);
  996.  
  997.     RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, &(ganiAcon.anih)), hf );
  998.  
  999.     /* if rate then write it */
  1000.     if (fRate) {
  1001.         rtag.ckID = FOURCC_rate;
  1002.         rtag.ckSize = cSteps * sizeof(JIF);
  1003.  
  1004.         RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, pjif), hf );
  1005.     }
  1006.  
  1007.     /* if seq, then write it */
  1008.     if (fSeq) {
  1009.         rtag.ckID = FOURCC_seq;
  1010.         rtag.ckSize = cSteps * sizeof(DWORD);
  1011.  
  1012.         RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, pseq), hf );
  1013.     }
  1014.  
  1015.     /* write the fram list */
  1016.     rtag.ckID = FOURCC_LIST;
  1017.     rtag.ckSize = cbFram;
  1018.  
  1019.     RET_CLOSE_IF_ERR( WriteTag(hf, &rtag), hf );
  1020.     RET_CLOSE_IF_ERR( WriteType(hf, FOURCC_fram), hf );
  1021.  
  1022.     for( i = 0; i < cFrames; i++ ) {
  1023.         RET_CLOSE_IF_ERR( WriteTagData(hf, &(pfrm[i]->rtag), pfrm[i]->abIcon),
  1024.                 hf);
  1025.     }
  1026.  
  1027.     /* Close the file */
  1028.     CloseHandle(hf);
  1029.  
  1030.     return TRUE;
  1031. }
  1032.  
  1033.  
  1034. /***************************************************************************\
  1035. * WriteTag, WriteType, WriteTagData
  1036. *
  1037. * Some handy functions for writing RIFF files.
  1038. *
  1039. * History:
  1040. *   30-Apr-1993 JonPa   Created them.
  1041. \***************************************************************************/
  1042. BOOL WriteTag(HANDLE hf, PRTAG prtag) {
  1043.     DWORD cbWritten;
  1044.  
  1045.     return (WriteFile(hf, prtag, sizeof(RTAG), &cbWritten, NULL) &&
  1046.             cbWritten == sizeof(RTAG));
  1047. }
  1048.  
  1049. BOOL WriteType(HANDLE hf, FOURCC ckID ) {
  1050.     DWORD cbWritten;
  1051.  
  1052.     return (WriteFile(hf, &ckID, sizeof(FOURCC), &cbWritten, NULL) &&
  1053.             cbWritten == sizeof(FOURCC));
  1054. }
  1055.  
  1056. BOOL WriteTagData(HANDLE hf, PRTAG prtag, VOID *pvData ) {
  1057.     DWORD cbWritten;
  1058.     DWORD cbWrite = PADUP(prtag->ckSize);
  1059.  
  1060.     return  WriteTag(hf, prtag) && WriteFile(hf, pvData, cbWrite,
  1061.             &cbWritten, NULL) && cbWritten == cbWrite;
  1062. }
  1063.  
  1064.  
  1065.  
  1066.  
  1067. /***************************************************************************\
  1068. * VOID SaveFile(HWND hwnd, BOOL fPrompt)
  1069. *
  1070. * Conditionally Prompt the user for a name and then save the file
  1071. *
  1072. * History:
  1073. *   04-May-1993 JonPa   It
  1074. \***************************************************************************/
  1075. VOID SaveFile(HWND hwnd, BOOL fPrompt) {
  1076.     TCHAR szFileTitle[MAX_PATH];
  1077.     HANDLE hf;
  1078.  
  1079.     szFileTitle[0] = TEXT('\0');
  1080.  
  1081.     if (fPrompt || ganiAcon.szFile[0] == TEXT('\0')) {
  1082. tryagain:
  1083.         if (!PromptForFile(hwnd, COUNTOF(szFileTitle), szFileTitle,
  1084.                 COUNTOF(ganiAcon.szFile), ganiAcon.szFile, gpszAniFilter,
  1085.                 NULL, TRUE)) {
  1086.             return;
  1087.         }
  1088.     }
  1089.  
  1090.     hf = CreateFile( ganiAcon.szFile, GENERIC_WRITE, 0, NULL,
  1091.             CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  1092.  
  1093.     if (hf == INVALID_HANDLE_VALUE) {
  1094.         FmtMessageBox(hwnd, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP, TRUE,
  1095.                 MSG_CANTCREATEFILE, ganiAcon.szFile);
  1096.  
  1097.         goto tryagain;
  1098.     }
  1099.  
  1100.     if( !SaveAniFile(hwnd, hf) ) {
  1101.         FmtMessageBox(hwnd, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP, TRUE,
  1102.                 MSG_FILEWRITEERR, ganiAcon.szFile);
  1103.         return;
  1104.     }
  1105.  
  1106.     if (szFileTitle[0] != TEXT('\0'))
  1107.         SetWindowFileTitle(hwnd, szFileTitle);
  1108.  
  1109.     ganiAcon.fDirty = FALSE;
  1110. }
  1111.