|
|
/****************************************************************************\
* MAKEANI - * * File: MAKEANI.c * * by Darrin Massena * * LATER: * ------ * - Chunks must be DWORD padded so tags & data are DWORD-aligned. * * History * ------- * 06-30-90 darrinm Wrote to build animation files for PM's AniPointers. * 10-01-91 darrinm Ported to Windows NT. * 08-09-92 darrinm Added frame sequence control. * 08-17-92 darrinm Changed to RIFF format. * 12-28-92 byrond Allowed both '-' and '/' on command line. * Renamed to makeani instead of makerad. * 03-39-92 jonpa Merged new RIFF code with old RAD version for NT \****************************************************************************/
#include <windows.h>
#include <winuserp.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <io.h>
#include <asdf.h> // BUGBUG LATER:incorporate this into winuserp.h
VOID PrintHelp(BOOL fFullHelp); BOOL QueryAniInfo(char *pszFile); BOOL AddFrame(char *pszFile, JIF jifRate); BOOL WriteAniFile(char *pszOutFile, char *pszTitle, char *pszAuthor);
BOOL ReadTag(FILE *hf, PRTAG ptag); BOOL WriteTag(FILE *hf, DWORD type, DWORD len); BOOL DumpList(FILE *hf, PRTAG ptag, char *pszIndent, BOOL fList);
#define WORDALIGN(n) ((n + 1) & 0xFFFFFFFE)
#define CFRMMAX 250 // max frames MAKEANI will concatenate
typedef struct _FRAME { // frm
char *pszFile; } FRAME, *PFRAME;
typedef struct _STEP { // step
int ifrm; JIF jifRate; } STEP, *PSTEP;
FRAME gafrm[CFRMMAX]; STEP gastep[CFRMMAX]; int gcfrm = 0; int gcstep = 0; BOOL gfSequenced = FALSE;
/*
* MAIN */ VOID _CRTAPI1 main(int argc, char **argv) { int i, j; JIF jifRate = 2; int cchT; DWORD fl = 0; BOOL fSwitches; char *pch; // disgusting use of stack space.
char szAuthor[256], szTitle[256], szOut[FILENAME_MAX]; BOOL fTitled = FALSE, fAuthored = FALSE;
// This is where the fun starts.
if (argc <= 1) { /*
* If we're passed no args, print help. */ PrintHelp(FALSE); goto lbExit; }
// Initialize a default output filename.
strcpy(szOut, "temp.ani");
for (i = 1; i < argc; i++) {
cchT = strlen(argv[i]); pch = argv[i]; fSwitches = FALSE;
for (j = 0; j < cchT; j++) {
if (!fSwitches) { if (pch[j] == '-' || pch[j] == '/') { fSwitches = TRUE; } else { // Add this file to the output ANI file.
AddFrame(argv[i], jifRate); j = cchT; // so we advance to the next arg after continue
} continue; }
// Parse the switches.
switch (pch[j]) { case '?': // Give a little friendly advice.
PrintHelp(TRUE); goto lbExit;
case 't': // get the image title
i++; strcpy(szTitle, argv[i]); fTitled = TRUE; break;
case 'a': i++; strcpy(szAuthor, argv[i]); fAuthored = TRUE; break;
case 'o': i++; // NOTE: no error check here
strcpy(szOut, argv[i]); break;
case 'q': // Query file info.
case 'i': // I just can't decide which is better
i++; QueryAniInfo(argv[i]); break;
case 'r': // This allows both '-r10' and '-r 10'
if ((pch[j+1] >= '0') && (pch[j+1] <= '9')) { jifRate = atoi(argv[i] + j + 1); j = cchT; } else { i++; jifRate = atoi(argv[i]); } break; } } }
if (gcfrm > 0) WriteAniFile(szOut, fTitled ? szTitle : NULL, fAuthored ? szAuthor : NULL);
lbExit: exit(0); }
BOOL AddFrame(char *pszFile, JIF jifRate) { int cch; char *psz; int ifrm;
// See if this frame already exists. If it does then add a step
// that references the first instance of this frame instead of
// adding a new frame.
for (ifrm = 0; ifrm < gcfrm; ifrm++) { if (_strcmpi(pszFile, gafrm[ifrm].pszFile) == 0) break; }
if (ifrm == gcfrm) {
cch = strlen(pszFile) + 1; psz = malloc(cch); if (psz == NULL) return FALSE;
strcpy(psz, pszFile); gafrm[ifrm].pszFile = psz; gcfrm++;
} else {
// Since we're referencing an existing frame, this animation
// must be sequenced!
gfSequenced = TRUE; }
gastep[gcstep].ifrm = ifrm; gastep[gcstep].jifRate = jifRate; gcstep++;
return TRUE; }
BOOL WriteAniFile(char *pszOutFile, char *pszTitle, char *pszAuthor) { int i; FILE *hfOut, *hfIn; ANIHEADER anih; JIF jifRate, *pjif; DWORD len, cbIn, ulNewPtr; char *pbIn, szT[80]; DWORD *pseq; long offICLst; DWORD cbICLst;
// Open the output file.
printf("MAKEANI: Opening output file \"%s\"...\r", pszOutFile);
hfOut = fopen(pszOutFile, "wb"); if (hfOut == NULL) return FALSE;
printf("MAKEANI: Writing header... \r");
// Write out the RIFF file identifier.
WriteTag(hfOut, FOURCC_RIFF, sizeof(DWORD)); len = FOURCC_ACON; fwrite(&len, 1, sizeof(DWORD), hfOut);
// If we have any info to add, write an INFO list.
if (pszTitle != NULL || pszAuthor != NULL) { len = 0; if (pszTitle != NULL) len += WORDALIGN(strlen(pszTitle) + 1) + sizeof(RTAG); if (pszAuthor != NULL) len += WORDALIGN(strlen(pszAuthor) + 1) + sizeof(RTAG);
WriteTag(hfOut, FOURCC_LIST, len + sizeof(DWORD)); len = FOURCC_INFO; fwrite(&len, 1, sizeof(DWORD), hfOut); }
// Write out the title string if one is passed in.
if (pszTitle != NULL) { printf("MAKEANI: Writing title... \n");
// Write the INAM (Name) RTAG.
len = strlen(pszTitle) + 1; // + 1 for 0 terminator.
WriteTag(hfOut, FOURCC_INAM, len);
// Write out the title string itself.
fwrite(pszTitle, 1, WORDALIGN(len), hfOut); }
// Write out the author string if one is passed in.
if (pszAuthor != NULL) { printf("MAKEANI: Writing author... \n");
// Write the IART (Artist) RTAG.
len = strlen(pszAuthor) + 1; // + 1 for 0 terminator.
WriteTag(hfOut, FOURCC_IART, len);
// Write out the author string itself.
fwrite(pszAuthor, 1, WORDALIGN(len), hfOut); }
// Write out the ANIHEADER RTAG.
WriteTag(hfOut, FOURCC_anih, sizeof(ANIHEADER));
// Write out the ANIHEADER.
anih.cbSizeof = sizeof(ANIHEADER); anih.cFrames = gcfrm; anih.cSteps = gcstep; anih.cx = anih.cy = anih.cBitCount = anih.cPlanes = 0; anih.jifRate = gastep[0].jifRate; if (gfSequenced) anih.fl = AF_ICON | AF_SEQUENCE; else anih.fl = AF_ICON; fwrite(&anih, 1, WORDALIGN(sizeof(ANIHEADER)), hfOut);
// Determine if all frames are to be played at the same rate. If not,
// write out a RATE chunk.
jifRate = gastep[0].jifRate; for (i = 1; i < gcstep; i++) { if (gastep[i].jifRate != jifRate) {
printf("MAKEANI: Writing RATE chunk...\n");
len = sizeof(JIF) * gcstep; WriteTag(hfOut, FOURCC_rate, len);
pjif = malloc(len); for (i = 0; i < gcstep; i++) pjif[i] = gastep[i].jifRate; fwrite(pjif, 1, WORDALIGN(len), hfOut); free(pjif); break; } }
// Write a sequence step array if the animation is sequenced.
if (gfSequenced) { printf("MAKEANI: Writing SEQ chunk...\n");
len = sizeof(DWORD) * gcstep; WriteTag(hfOut, FOURCC_seq, len);
pseq = malloc(len); for (i = 0; i < gcstep; i++) pseq[i] = gastep[i].ifrm; fwrite(pseq, 1, WORDALIGN(len), hfOut); free(pseq); }
// Write out the ICON List */
WriteTag(hfOut, FOURCC_LIST, 0); offICLst = ftell(hfOut); len = FOURCC_fram; fwrite(&len, 1, sizeof(DWORD), hfOut);
for (i = 0; i < gcfrm; i++) {
// Note the spaces for clearing to the end of the previous line.
printf("MAKEANI: Reading frame %3d: \"%s\"", i, gafrm[i].pszFile);
// Find out how big the file is so we can read the whole thing in.
hfIn = fopen(gafrm[i].pszFile, "rb"); if (hfIn == NULL) {
// Maybe the user just forgot to give the .CUR file extension.
strcpy(szT, gafrm[i].pszFile); strcat(szT, ".cur"); hfIn = fopen(szT, "rb"); if (hfIn == NULL) { printf("...failed\n"); continue; } } printf("\r");
cbIn = _filelength(_fileno(hfIn));
// Allocate buffer to read the mouse pointer images into.
pbIn = malloc(WORDALIGN(cbIn)); if (pbIn == NULL) { fclose(hfIn); printf("...out of memory\n"); continue; }
// Read the mouse pointer image in.
fread(pbIn, 1, cbIn, hfIn); fclose(hfIn);
printf("MAKEANI: Writing frame %3d: \"%s\"\n", i, gafrm[i].pszFile);
WriteTag(hfOut, FOURCC_icon, cbIn); fwrite(pbIn, 1, WORDALIGN(cbIn), hfOut);
// Free up that mouse pointer buffer.
free(pbIn); }
// Backpatch length of file, and frame list length
cbICLst = ftell(hfOut) - offICLst;
printf("MAKEANI: Back-patching lengths... \r"); len = ftell(hfOut) - sizeof(RTAG); fseek(hfOut, sizeof(DWORD), SEEK_SET); fwrite(&len, 1, sizeof(DWORD), hfOut);
fseek(hfOut, offICLst - sizeof(DWORD), SEEK_SET); fwrite(&cbICLst, 1, sizeof(DWORD), hfOut);
fclose(hfOut);
printf("MAKEANI: Done. \n");
return TRUE; }
BOOL QueryAniInfo(char *pszFile) { FILE *hf; char szIndent[80]; RTAG tag; DWORD dw;
szIndent[0] = 0;
hf = fopen(pszFile, "rb"); if (hf == NULL) { printf("MAKEANI: Can't open \"%s\".\n", pszFile); return FALSE; }
ReadTag(hf, &tag);
// First tag must always be RIFF.
if (tag.ckID != FOURCC_RIFF) { printf("MAKEANI: \"%s\" is not a valid RIFF file.\n", pszFile); fclose(hf); return FALSE; }
printf("\n\"%s\"\n", pszFile);
DumpList(hf, &tag, szIndent, FALSE);
fclose(hf); return TRUE; }
BOOL DumpList(FILE *hf, PRTAG ptag, char *pszIndent, BOOL fList) { int len; char szType[5] = "TEMP"; char *pbT;
fread(szType, 1, sizeof(DWORD), hf); if (fList) { printf("%sLIST %s (%d)\n", pszIndent, szType, ptag->ckSize); len = WORDALIGN(ptag->ckSize) - sizeof(DWORD); } else { printf("%sRIFF %s (%d)\n", pszIndent, szType, ptag->ckSize); len = 1000000000; }
strcat(pszIndent, " ");
while (len > 0) { if (!ReadTag(hf, ptag)) return FALSE;
len -= WORDALIGN(ptag->ckSize) + sizeof(RTAG);
if (ptag->ckID == FOURCC_LIST) {
// Recurse on lists.
DumpList(hf, ptag, pszIndent, TRUE);
} else { *(DWORD *)szType = ptag->ckID; printf("%s%s (%d)\t", pszIndent, szType, ptag->ckSize);
if ((ptag->ckID == FOURCC_INAM) || (ptag->ckID == FOURCC_IART)) { pbT = malloc(WORDALIGN(ptag->ckSize)); fread(pbT, 1, WORDALIGN(ptag->ckSize), hf); printf("\"%s\"\n", pbT); free(pbT); } else { printf("\n"); fseek(hf, WORDALIGN(ptag->ckSize), SEEK_CUR); } } }
pszIndent[strlen(pszIndent) - 4] = 0; return TRUE; }
VOID PrintHelp(BOOL fFullHelp) { printf("Microsoft(R) Animated Cursor Tool Version 1.0\n"); printf("(C) 1993 Microsoft Corp. All rights reserved.\n\n"); printf("Usage:\n"); printf(" makeani [-?] [-t <title>] [-a <author>] [-o <outfile>] [-r #] [<file> ...]\n");
if (!fFullHelp) return;
printf("\nOptions:\n"); printf(" -o <outfile>: Designates output file.\n"); printf(" -q <file> : Queries an existing ANI file, displaying file info.\n"); printf(" -t <title> : Put title string in output file.\n"); printf(" -a <author> : Put author string in output file.\n"); printf(" -r # : Set inter-frame delay rate (in jiffies - 1/60 sec) for all\n"); printf(" frames following. This option may be repeated multiple times.\n"); printf(" If unspecified, the rate defaults to 1 (fastest possible).\n"); printf(" -? : Print this help message.\n\n"); printf("Example:\n"); printf(" makeani -t \"New\" -o new.ani -r 4 frame1.cur frame2.cur -r 20 frame3.cur\n");
}
BOOL ReadTag(FILE *hf, PRTAG ptag) { ptag->ckID = ptag->ckSize = 0L;
if (fread(ptag, 1, sizeof(RTAG), hf) != 0) return TRUE; else return FALSE; }
BOOL WriteTag(FILE *hf, DWORD type, DWORD len) { RTAG tag;
tag.ckID = type; tag.ckSize = len;
if (fwrite(&tag, 1, sizeof(RTAG), hf) != 0) return TRUE;
return FALSE; }
|