/****************************************************************************\
*
* MODULE: anifile.c
*
* PURPOSE: Processes files for the Animated Cursor Editor
*
* Copyright 1993-1996 Microsoft Corp.
*
*
* History:
* 21-Apr-1993 JonPa Wrote it.
*
\****************************************************************************/
#include <windows.h>
#include <commdlg.h>
#include "anidefs.h"
/****************************************************************************\
*
* FUNCTION: BOOL CreateFrameFromCursorFile( LPSTR pszFile )
*
* PURPOSE: Opens a cursor file, reads the icon info out of it,
* and creates a frame and step for that icon, then links
* everything together and updates the listbox.
*
* NOTES: This function accesses the global flag gfEditFrame.
* If this bool is TRUE, then the currently selected frame
* in the listbox is overwritten. If it is false, then
* a new frame is created and inserted after the currently
* selected frame (or at the end if no selection).
*
* History:
* 21-Apr-1993 JonPa Created it
*
\****************************************************************************/
BOOL CreateFrameFromCursorFile(HWND hwnd, LPTSTR pszFile, BOOL fEdit) {
PFRAME pf;
HANDLE hf;
PSTEP psOld, psNew;
DWORD ckSize;
int iSel;
int cSel;
cSel = GetSelStepCount(hwnd);
if ( (fEdit && (cSel != 1)) || cSel > 1) {
FmtMessageBox( hwnd, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP,
TRUE, fEdit ? MSG_MUSTEQONEFAME : MSG_LESSEQONEFRAME);
return FALSE;
}
/* get currently selected frame */
GetCurrentSel(hwnd, DLG_MAIN_FRAMELIST, &iSel, 1, &cSel );
if (cSel == 0)
psOld = NULL;
else
psOld = GetStep(hwnd, iSel);
/*
* If not editing, create a new step
*/
if (!fEdit || !IsValidPS(psOld)) {
psNew = NewStep();
if (psNew == NULL) {
return FALSE;
}
} else {
psNew = NULL;
}
hf = CreateFile(pszFile, GENERIC_READ,
0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hf == INVALID_HANDLE_VALUE)
return FALSE;
ckSize = GetFileSize(hf, NULL);
/* get the frame out of the file */
pf = ReadIconFromFile(hwnd, hf, ckSize);
CloseHandle(hf);
if (pf == NULL) {
if (psNew != NULL)
FreeMem(psNew);
return FALSE;
}
if (psNew != NULL) {
if (IsValidPS(psOld)) {
psNew->jif = psOld->jif;
iSel += 1;
} else {
psNew->jif = ganiAcon.anih.jifRate;
iSel = SendDlgItemMessage(hwnd, DLG_MAIN_FRAMELIST, LB_GETCOUNT,
0, 0);
}
LinkStepFrame(psNew, pf);
SendDlgItemMessage(hwnd, DLG_MAIN_FRAMELIST, LB_INSERTSTRING, iSel,
(LPARAM)psNew);
SetCurrentSel(hwnd, DLG_MAIN_FRAMELIST, FALSE, iSel);
} else {
HWND hwndLB = GetDlgItem(hwnd, DLG_MAIN_FRAMELIST);
/*
* Delete the old frame and point the step to the new one.
*/
LinkStepFrame(psOld, pf);
InvalidateRect(hwndLB, NULL, TRUE);
}
return TRUE;
}
/****************************************************************************\
*
* FUNCTION: HICON ConvertDataToIcon( PFRAME pf )
*
* PURPOSE:
*
*
*
*
* History:
* 23-Apr-1993 JonPa copied from Win NT USERs ReadIconGuts
*
\****************************************************************************/
HICON ConvertDataToIcon( PFRAME pf, WORD *pxHotSave, WORD *pyHotSave )
{
NEWHEADER *pnh;
NEWHEADER *pnhBase;
RESDIR *prd;
int offMatch;
ICONFILERESDIR *pird;
PCURSORRESOURCE pcres;
BOOL fIcon;
HICON hicon;
WORD x, y;
LPBYTE pbBits;
pnhBase = (NEWHEADER *)pf->abIcon;
/*
* Construct a fake array of RESDIR entries using the info at the head
* of the file. Store the data offset in the idIcon WORD so it can be
* returned by RtlGetIdFromDirectory.
*/
pnh = (NEWHEADER *)LocalAlloc(LMEM_FIXED, sizeof(NEWHEADER) +
(pnhBase->cResources * sizeof(RESDIR)));
if (pnh == NULL)
return NULL;
*pnh = *pnhBase;
prd = (RESDIR *)(pnh + 1);
pird = (ICONFILERESDIR *)(pnhBase + 1);
/* prime pird for first line of loop */
pird--;
for (offMatch = 0; offMatch < (int)pnh->cResources; offMatch++, prd++) {
/*
* Get the next resource directory from the icon file.
*/
++pird;
/*
* Convert from the icon editor's resource directory format
* to the post-RC.EXE format LookupIconIdFromDirectory expects.
*/
if (pnh->rt == 1) { // ICON
prd->ResInfo.Icon.Width = pird->bWidth;
prd->ResInfo.Icon.Height = pird->bHeight;
prd->ResInfo.Icon.ColorCount = pird->bColorCount;
prd->ResInfo.Icon.reserved = 0;
} else { // CURSOR
prd->ResInfo.Cursor.Width = pird->bWidth;
prd->ResInfo.Cursor.Height = pird->bHeight;
}
prd->Planes = 0; // Hopefully nobody uses this
prd->BitCount = 0; // " "
prd->BytesInRes = pird->dwDIBSize;
prd->idIcon = (WORD)pird->dwDIBOffset;
}
/*
* NOTE: nh.rt is NOT an RT_ type value. For instance, nh.rt == 1 for
* an icon file where as 1 == RT_CURSOR, not RT_ICON.
*/
fIcon = (pnhBase->rt == 1);
offMatch = LookupIconIdFromDirectory((PBYTE)pnh, fIcon);
LocalFree(pnh);
if (fIcon) {
pcres = (PCURSORRESOURCE)&(pf->abIcon[offMatch]);
*pxHotSave = gcxCursor / 2;
*pyHotSave = gcyCursor / 2;
} else {
offMatch -= (sizeof(pcres->xHotspot) + sizeof(pcres->yHotspot));
for(; pird->dwDIBOffset != (WORD)offMatch &&
pird != (ICONFILERESDIR *)(pnhBase + 1); pird--);
pcres = (PCURSORRESOURCE)&(pf->abIcon[offMatch]);
x = pcres->xHotspot;
y = pcres->yHotspot;
*pxHotSave = pcres->xHotspot = pird->xHotspot;
*pyHotSave = pcres->yHotspot = pird->yHotspot;
}
// Buffer must be aligned
pbBits = LocalAlloc(LMEM_FIXED, pf->rtag.ckSize - offMatch );
if (pbBits) {
CopyMemory( pbBits, pcres, pf->rtag.ckSize - offMatch );
hicon = CreateIconFromResource( pbBits,
pf->rtag.ckSize - offMatch, fIcon, 0x00030000);
LocalFree( pbBits );
} else
hicon = NULL;
if(!fIcon) {
pcres->xHotspot = x;
pcres->yHotspot = y;
}
return hicon;
}
/****************************************************************************\
*
* FUNCTION: PFRAME ReadIconFromFile(HWND hwnd, HANDLE hf, DWORD ckSize)
*
* PURPOSE: Reads the icon info out of a file,
* and creates a frame for that icon.
*
*
* History:
* 22-Apr-1993 JonPa Created it
*
\****************************************************************************/
PFRAME ReadIconFromFile(HWND hwnd, HANDLE hf, DWORD ckSize) {
PFRAME pf = AllocMem( sizeof( FRAME ) + ckSize );
DWORD cbRead;
PFRAME pfList;
if (pf != NULL) {
pf->cRef = 0;
if (ReadFile(hf, pf->abIcon, ckSize, &cbRead, NULL) &&
cbRead == ckSize) {
/* got the data, now set up the rest of the frame and link it in */
pf->dwCheckSum = CalcCheckSum( pf->abIcon, ckSize );
pf->rtag.ckID = FOURCC_icon;
pf->rtag.ckSize = ckSize;
/* Check if this fram is already in the list */
for (pfList = gpfrmFrames; pfList != NULL;
pfList = pfList->pfrmNext ) {
if (pf->dwCheckSum == pfList->dwCheckSum &&
pf->rtag.ckSize == pfList->rtag.ckSize &&
memcmp( pf->abIcon, pfList->abIcon, ckSize ) == 0) {
/*
* These frames are the same, coalesce them into a
* sequence.
*/
FreeMem(pf);
pf = pfList;
break;
}
}
if (pfList == NULL) {
/*
* Did not find a dup, create an icon for this frame
*/
pf->hcur = ConvertDataToIcon( pf, &(pf->xHotSpot),
&(pf->yHotSpot) );
pf->pfrmNext = gpfrmFrames;
gpfrmFrames = pf;
}
} else {
/* File Error */
FreeMem(pf);
pf = NULL;
}
}
return pf;
}
/****************************************************************************\
*
* FUNCTION: HANDLE PromptAndOpenFile( )
*
* PURPOSE: Pust up the standard open dialog and then opens the file
*
*
*
*
* History:
* 21-Apr-1993 JonPa Created it
*
\****************************************************************************/
HANDLE PromptAndOpenFile(
HWND hwnd,
DWORD cchFileTitle,
LPTSTR pszFileTitle,
DWORD cchFileName,
LPTSTR pszFileName,
LPTSTR pszFilter
)
{
HANDLE hf = INVALID_HANDLE_VALUE;
if (PromptForFile( hwnd, cchFileTitle, pszFileTitle, cchFileName,
pszFileName, pszFilter, NULL, FALSE )) {
/* Open the file. */
hf = CreateFile(pszFileName, GENERIC_READ,
0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hf == INVALID_HANDLE_VALUE) {
FmtMessageBox( hwnd, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP, TRUE,
MSG_CANTOPENFILE, pszFileName );
}
}
return hf;
}
/****************************************************************************\
*
* FUNCTION: HANDLE PromptForFile( )
*
* PURPOSE: Pust up the standard open dialog
*
*
*
*
* History:
* 28-Apr-1993 JonPa Created it from PromptAndOpenFile
*
\****************************************************************************/
BOOL PromptForFile(
HWND hwnd,
DWORD cchFileTitle,
LPTSTR pszFileTitle,
DWORD cchFileName,
LPTSTR pszFile,
LPTSTR pszFilter,
LPTSTR pszDlgTitle,
BOOL fSave
)
{
OPENFILENAME ofn;
ZeroMemory(&ofn, sizeof(ofn));
/* Set the members of the OPENFILENAME structure. */
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = pszFilter;
ofn.nFilterIndex = 0;
ofn.lpstrFile = pszFile;
ofn.nMaxFile = cchFileName;
ofn.lpstrFileTitle = pszFileTitle;
ofn.nMaxFileTitle = cchFileTitle;
ofn.lpstrTitle = pszDlgTitle;
ofn.lpstrDefExt = gpszANI;
if (fSave) {
ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
} else {
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
}
/* Display the SaveAs or Open dialog box. */
return fSave ? GetSaveFileName(&ofn) : GetOpenFileName(&ofn);
}
/****************************************************************************\
*
* FUNCTION: BOOL ReadAniFile( HWND hwnd, HANDLE hf ) {
*
* PURPOSE:
&
* Loads an animatied cursor from a RIFF file. The RIFF file format for
* animated cursors looks like this:
*
* RIFF( 'ACON'
* LIST( 'INFO'
* INAM( <name> )
* IART( <artist> )
* )
* anih( <anihdr> )
* [rate( <rateinfo> ) ]
* ['seq '( <seq_info> )]
* LIST( 'fram' icon( <icon_file> ) )
* )
*
*
* History:
* 02-Oct-1991 DarrinM Created. (in Win32 user)
* 17-Mar-1993 JonPa Rewrote to use RIFF format instead of RAD
* 21-Apr-1993 JonPa Copied it to anifile.c and tweeked it.
*
\****************************************************************************/
BOOL ReadAniFile( HWND hwnd, HANDLE hf ) {
RTAG tag;
DWORD cbRead;
BOOL fSuccess = FALSE;
JIF *pjifRate = NULL;
DWORD *pseq = NULL;
PFRAME *ppfram = NULL;
int iFrame = 0;
int i;
if (!ReadTag(hf, &tag))
goto laiFileErr;
/*
* Make sure it's a RIFF ANI file
*/
if (tag.ckID != FOURCC_RIFF)
goto laiFileErr;
/* read the chunk type */
if(!ReadFile(hf, &tag.ckID, sizeof(tag.ckID), &cbRead, NULL) ||
cbRead < sizeof(tag.ckID)) {
goto laiFileErr;
}
if (tag.ckID != FOURCC_ACON)
goto laiFileErr;
/* look for 'anih', 'rate', 'seq ', and 'icon' chunks */
while( ReadTag(hf, &tag)) {
switch( tag.ckID ) {
case FOURCC_anih:
if (!ReadChunk(hf, &tag, &ganiAcon.anih))
goto laiFileErr;
if (!(ganiAcon.anih.fl & AF_ICON) || (ganiAcon.anih.cFrames == 0))
goto laiFileErr;
/*
* Allocate space for the ANIHEADER, and a seq and
* rate table (in case we run into one later).
*/
pjifRate = AllocMem( ganiAcon.anih.cSteps * sizeof(JIF) +
ganiAcon.anih.cSteps * sizeof(DWORD) +
ganiAcon.anih.cSteps * sizeof(PFRAME));
if (pjifRate == NULL)
goto laiFileErr;
pseq = (DWORD *)(pjifRate + ganiAcon.anih.cSteps);
ppfram = (PFRAME *)(pseq + ganiAcon.anih.cSteps);
for( i = 0; i < (int)ganiAcon.anih.cSteps; i++ ) {
pjifRate[i] = ganiAcon.anih.jifRate;
pseq[i] = i;
ppfram[i] = NULL;
}
break;
case FOURCC_rate:
/*
* If we find a rate chunk, read it into its preallocated
* space.
*/
if(!ReadChunk(hf, &tag, (PBYTE)pjifRate))
goto laiFileErr;
break;
case FOURCC_seq:
/*
* If we find a seq chunk, read it into its preallocated
* space.
*/
if(!ReadChunk(hf, &tag, (PBYTE)pseq))
goto laiFileErr;
break;
case FOURCC_LIST: {
DWORD cbChunk = PADUP(tag.ckSize);
/*
* See if this list is the 'fram' list of icon chunks
*/
if(!ReadFile(hf, &tag.ckID, sizeof(tag.ckID), &cbRead, NULL) ||
cbRead < sizeof(tag.ckID)) {
goto laiFileErr;
}
cbChunk -= cbRead;
if (tag.ckID == FOURCC_fram) {
while(cbChunk >= sizeof(tag)) {
if (!ReadTag(hf, &tag))
goto laiFileErr;
cbChunk -= sizeof(tag);
if(tag.ckID == FOURCC_icon) {
PFRAME pfrm;
/*
* Ok, load the icon/cursor bits,
*/
pfrm = ReadIconFromFile(hwnd, hf, tag.ckSize);
if (pfrm == NULL) {
goto laiFileErr;
}
for( i = 0; i < (int)ganiAcon.anih.cSteps; i++ ) {
if (pseq[i] == (DWORD)iFrame) {
ppfram[i] = pfrm;
}
}
iFrame++;
} else {
/*
* Unknown chunk in fram list, just ignore it
*/
SkipChunk(hf, &tag);
}
cbChunk -= PADUP(tag.ckSize);
}
} else if (tag.ckID == FOURCC_INFO) {
/* now look for INAM and IART chunks */
while( cbChunk >= sizeof(tag) ) {
if (!ReadTag(hf, &tag))
goto laiFileErr;
cbChunk -= sizeof(tag);
switch( tag.ckID ) {
case FOURCC_INAM:
if (cbChunk < tag.ckSize ||
!ReadChunkN(hf, &tag, ganiAcon.azTitle,
sizeof(ganiAcon.azTitle)))
goto laiFileErr;
cbChunk -= PADUP(tag.ckSize);
break;
case FOURCC_IART:
if (cbChunk < tag.ckSize ||
!ReadChunkN(hf, &tag, ganiAcon.azCreator,
sizeof(ganiAcon.azCreator)))
goto laiFileErr;
cbChunk -= PADUP(tag.ckSize);
break;
default:
if (!SkipChunk( hf, &tag ))
goto laiFileErr;
cbChunk -= PADUP(tag.ckSize);
break;
}
}
} else {
/*
* Not the fram list or the INFO list. Skip
* the rest of this chunk. (Don't forget that we have
* already skipped one dword!)
*/
tag.ckSize = cbChunk;
SkipChunk(hf, &tag);
break;
}
break;
}
default:
/*
* We're not interested in this chunk, skip it.
*/
if(!SkipChunk(hf, &tag))
goto laiFileErr;
break;
}
}
/*
* Update the frame count incase we coalesced some frames while reading
* in the file.
*/
ganiAcon.anih.cFrames = iFrame;
/*
* Now build up the listbox
*/
for( i = 0; i < (int)ganiAcon.anih.cSteps; i++ ) {
PSTEP ps;
ps = NewStep();
if (ps == NULL)
goto laiFileErr;
ps->jif = pjifRate[i];
LinkStepFrame(ps, ppfram[i]);
SendDlgItemMessage(hwnd, DLG_MAIN_FRAMELIST, LB_INSERTSTRING, i,
(LPARAM)ps);
}
SetDlgItemText(hwnd, DLG_MAIN_TITLE, ganiAcon.azTitle);
SetDlgItemText(hwnd, DLG_MAIN_AUTHOR, ganiAcon.azCreator);
SendDlgItemMessage(hwnd, DLG_MAIN_PREVIEW, PM_NEWCURSOR, 0, 0);
fSuccess = TRUE;
laiFileErr:
if (pjifRate != NULL)
FreeMem(pjifRate);
if (!fSuccess)
NewAniCursor(hwnd);
CloseHandle(hf);
return fSuccess;
}
/***************************************************************************\
* DWORD CalcCheckSum( PBYTE pb );
*
*
* History:
*
* 23-Apr-1993 JonPa Created.
\***************************************************************************/
DWORD CalcCheckSum( PBYTE pb, DWORD cb ) {
DWORD dw = 0;
while(cb--)
dw += (DWORD)*pb++;
return dw;
}
/***************************************************************************\
* ReadTag, ReadChunk, SkipChunk
*
* Some handy functions for reading RIFF files.
*
* History:
* 10-02-91 DarrinM Created.
* 03-25-93 Jonpa Changed to use RIFF format instead of ASDF
* 23-Apr-1993 JonPa Copied from Win NT USER.
\***************************************************************************/
BOOL ReadTag(
HANDLE hf,
PRTAG ptag)
{
DWORD cbActual;
ptag->ckID = ptag->ckSize = 0L;
if (!ReadFile(hf, ptag, sizeof(RTAG), &cbActual, NULL) ||
(cbActual != sizeof(RTAG)))
return FALSE;
/* no need to align file pointer since RTAG is already word aligned */
return TRUE;
}
BOOL ReadChunk(
HANDLE hf,
PRTAG ptag,
PVOID pv)
{
DWORD cbActual;
if (!ReadFile(hf, pv, ptag->ckSize, &cbActual, NULL) ||
(cbActual != ptag->ckSize))
return FALSE;
/* WORD align file pointer */
if( ptag->ckSize & 1 )
SetFilePointer(hf, 1, NULL, FILE_CURRENT);
return TRUE;
}
BOOL ReadChunkN(
HANDLE hf,
PRTAG ptag,
PVOID pv,
DWORD cbMax)
{
DWORD cbActual;
DWORD cbRead = min( cbMax, ptag->ckSize );
if (!ReadFile(hf, pv, ptag->ckSize, &cbActual, NULL) ||
(cbActual != cbRead))
return FALSE;
/* WORD align file pointer */
cbRead = ptag->ckSize - cbActual;
if( ptag->ckSize & 1 )
cbRead++;
return SetFilePointer(hf, cbRead, NULL, FILE_CURRENT) != 0xFFFFFFFF;
}
BOOL SkipChunk(
HANDLE hf,
PRTAG ptag)
{
/* Round ptag->ckSize up to nearest word boundary to maintain alignment */
return SetFilePointer(hf, PADUP(ptag->ckSize), NULL, FILE_CURRENT) !=
0xFFFFFFFFL;
}
/****************************************************************************\
*
* FUNCTION: VOID GetTempCursorFileName( szFileName );
*
* PURPOSE: Create a temporary .cur filename
*
*
* History:
* 22-Apr-1993 JonPa Created it
*
\****************************************************************************/
BOOL GetTempCursorFileName( LPTSTR pszName ) {
TCHAR szPath[MAX_PATH];
if( GetTempPath( MAX_PATH, szPath ) >= MAX_PATH )
lstrcpy( pszName, TEXT(".") );
return GetTempFileName(szPath, TEXT("ae"), 0, pszName) != 0;
}
/****************************************************************************\
*
* FUNCTION: BOOL SaveAniFile( HWND hwnd, HANDLE hf )
*
* PURPOSE:
&
* Saves an animatied cursor to a RIFF file. The RIFF file format for
* animated cursors looks like this:
*
* RIFF( 'ACON'
* [LIST( 'INFO'
* [INAM( <name> )]
* [IART( <artist> )]
* )]
* anih( <anihdr> )
* [rate( <rateinfo> ) ]
* ['seq '( <seq_info> )]
* LIST( 'fram' icon( <icon_file> ) )
* )
*
*
* History:
* 29-Apr-1993 JonPa Created it.
*
\****************************************************************************/
BOOL SaveAniFile( HWND hwnd, HANDLE hf ) {
int cSteps, i;
int cFrames;
PFRAME pf;
DWORD cbFile, cbFram, cbINFO, cbTitle, cbAuthor;
BOOL fRate, fSeq;
RTAG rtag;
PJIF pjif;
DWORD *pseq;
PFRAME *pfrm;
fRate = fSeq = FALSE;
cbINFO = cbFram = cbFile = cbTitle = cbAuthor = 0;
PausePreview(hwnd, DLG_MAIN_PREVIEW);
cSteps = GetStepCount(hwnd);
if( cSteps == LB_ERR ) {
FmtMessageBox( ghwndMain, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP,
TRUE, MSG_OUTOFRESOUCES );
return FALSE;
}
cFrames = 0;
for( pf = gpfrmFrames; pf != NULL; pf = pf->pfrmNext ) {
pf->iFrame = -1;
cFrames++;
}
ganiAcon.anih.cSteps = cSteps;
pjif = AllocMem( (sizeof(JIF) + sizeof(DWORD) + sizeof(PFRAME)) * cSteps );
if(pjif == NULL)
return FALSE;
pseq = (DWORD *)&pjif[cSteps];
pfrm = (PFRAME *)&pseq[cSteps];
cFrames = 0;
for( i = 0; i < cSteps; i++ ) {
PSTEP ps;
ps = GetStep(hwnd, i);
if( IsValidPS(ps) ) {
if (ps->pfrmFrame->iFrame == -1) {
cbFram += sizeof(RTAG);
cbFram += PADUP(ps->pfrmFrame->rtag.ckSize);
ps->pfrmFrame->iFrame = cFrames;
pfrm[cFrames++] = ps->pfrmFrame;
} else
fSeq = TRUE;
pseq[i] = ps->pfrmFrame->iFrame;
if ((pjif[i] = ps->jif) != ganiAcon.anih.jifRate) {
fRate = TRUE;
}
}
}
ganiAcon.anih.cbSizeof = sizeof(ganiAcon.anih);
ganiAcon.anih.cFrames = cFrames;
ganiAcon.anih.fl = AF_ICON | (fSeq ? AF_SEQUENCE : 0);
cbTitle = GetDlgItemTextA(hwnd, DLG_MAIN_TITLE, ganiAcon.azTitle,
COUNTOF(ganiAcon.azTitle));
cbAuthor = GetDlgItemTextA(hwnd, DLG_MAIN_AUTHOR, ganiAcon.azCreator,
COUNTOF(ganiAcon.azCreator));
/*
* At this point, cbFram == the size required by all the frames,
* add in the rate, seq, anih, and INFO list sizes as well as
* all the other overhead.
*/
cbFram += sizeof(FOURCC); //fram type
cbFile = cbFram;
cbFile += sizeof(FOURCC) + //ACON type
sizeof(RTAG) + //anih tag
PADUP(sizeof(ANIHEADER)) +
sizeof(RTAG); //LIST tag (for fram list)
if( cbTitle || cbAuthor) {
/*
* Remember, azCreator, and azTitle are ANSI strings!
*/
if( cbTitle ) {
cbTitle += 1; //add in ASCIIZ terminator
cbINFO += sizeof(RTAG) + //INAM tag
PADUP( cbTitle * sizeof(char));
}
if (cbAuthor) {
cbAuthor += 1; //add in ASCIIZ terminator
cbINFO += sizeof(RTAG) + //IART tag
PADUP(cbAuthor * sizeof(char));
}
cbINFO += sizeof(FOURCC); //INFO type
cbFile += sizeof(RTAG) + //LIST tag
cbINFO;
}
if (fSeq) {
cbFile += sizeof(RTAG) + //seq tag
PADUP(cSteps * sizeof(DWORD));
}
if (fRate) {
cbFile += sizeof(RTAG) + //rate tag
PADUP(cSteps * sizeof(JIF));
}
/*
* Now we have all the structures built in memory, it's time to
* write them out in RIFF ACON format!
*/
rtag.ckID = FOURCC_RIFF;
rtag.ckSize = cbFile;
RET_CLOSE_IF_ERR( WriteTag(hf, &rtag), hf );
RET_CLOSE_IF_ERR( WriteType(hf, FOURCC_ACON), hf );
if( cbTitle || cbAuthor) {
rtag.ckID = FOURCC_LIST;
rtag.ckSize = cbINFO;
RET_CLOSE_IF_ERR( WriteTag(hf, &rtag), hf );
RET_CLOSE_IF_ERR( WriteType(hf, FOURCC_INFO), hf );
if (cbTitle) {
rtag.ckID = FOURCC_INAM;
rtag.ckSize = cbTitle;
RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, ganiAcon.azTitle), hf );
}
if (cbAuthor) {
rtag.ckID = FOURCC_IART;
rtag.ckSize = cbAuthor;
RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, ganiAcon.azCreator), hf );
}
}
/* write anih */
rtag.ckID = FOURCC_anih;
rtag.ckSize = sizeof(ganiAcon.anih);
RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, &(ganiAcon.anih)), hf );
/* if rate then write it */
if (fRate) {
rtag.ckID = FOURCC_rate;
rtag.ckSize = cSteps * sizeof(JIF);
RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, pjif), hf );
}
/* if seq, then write it */
if (fSeq) {
rtag.ckID = FOURCC_seq;
rtag.ckSize = cSteps * sizeof(DWORD);
RET_CLOSE_IF_ERR( WriteTagData(hf, &rtag, pseq), hf );
}
/* write the fram list */
rtag.ckID = FOURCC_LIST;
rtag.ckSize = cbFram;
RET_CLOSE_IF_ERR( WriteTag(hf, &rtag), hf );
RET_CLOSE_IF_ERR( WriteType(hf, FOURCC_fram), hf );
for( i = 0; i < cFrames; i++ ) {
RET_CLOSE_IF_ERR( WriteTagData(hf, &(pfrm[i]->rtag), pfrm[i]->abIcon),
hf);
}
/* Close the file */
CloseHandle(hf);
return TRUE;
}
/***************************************************************************\
* WriteTag, WriteType, WriteTagData
*
* Some handy functions for writing RIFF files.
*
* History:
* 30-Apr-1993 JonPa Created them.
\***************************************************************************/
BOOL WriteTag(HANDLE hf, PRTAG prtag) {
DWORD cbWritten;
return (WriteFile(hf, prtag, sizeof(RTAG), &cbWritten, NULL) &&
cbWritten == sizeof(RTAG));
}
BOOL WriteType(HANDLE hf, FOURCC ckID ) {
DWORD cbWritten;
return (WriteFile(hf, &ckID, sizeof(FOURCC), &cbWritten, NULL) &&
cbWritten == sizeof(FOURCC));
}
BOOL WriteTagData(HANDLE hf, PRTAG prtag, VOID *pvData ) {
DWORD cbWritten;
DWORD cbWrite = PADUP(prtag->ckSize);
return WriteTag(hf, prtag) && WriteFile(hf, pvData, cbWrite,
&cbWritten, NULL) && cbWritten == cbWrite;
}
/***************************************************************************\
* VOID SaveFile(HWND hwnd, BOOL fPrompt)
*
* Conditionally Prompt the user for a name and then save the file
*
* History:
* 04-May-1993 JonPa It
\***************************************************************************/
VOID SaveFile(HWND hwnd, BOOL fPrompt) {
TCHAR szFileTitle[MAX_PATH];
HANDLE hf;
szFileTitle[0] = TEXT('\0');
if (fPrompt || ganiAcon.szFile[0] == TEXT('\0')) {
tryagain:
if (!PromptForFile(hwnd, COUNTOF(szFileTitle), szFileTitle,
COUNTOF(ganiAcon.szFile), ganiAcon.szFile, gpszAniFilter,
NULL, TRUE)) {
return;
}
}
hf = CreateFile( ganiAcon.szFile, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hf == INVALID_HANDLE_VALUE) {
FmtMessageBox(hwnd, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP, TRUE,
MSG_CANTCREATEFILE, ganiAcon.szFile);
goto tryagain;
}
if( !SaveAniFile(hwnd, hf) ) {
FmtMessageBox(hwnd, TITL_ERROR, NULL, MB_OK | MB_ICONSTOP, TRUE,
MSG_FILEWRITEERR, ganiAcon.szFile);
return;
}
if (szFileTitle[0] != TEXT('\0'))
SetWindowFileTitle(hwnd, szFileTitle);
ganiAcon.fDirty = FALSE;
}