/***************************************************************************** * * LoadAPF(char *filename, LocInfo *locInfo, Word *colorTable) * * Uses GS/OS native routines to read in an APF file * *****************************************************************************/ #include #include #include #include #include #include #include #include #pragma lint -1 #include "apf.h" /* Block Type strings */ #define NUM_TYPES 6 typedef struct { Word type; unsigned char len; char *kind; } BlockType; /* These are the recognized APF blocks */ BlockType blockTypes[NUM_TYPES] = { { MAIN, 4, "MAIN" }, { PATS, 4, "PATS" }, { SCIB, 4, "SCIB" }, { PALETTES, 7, "PALETTES" }, { MASK, 4, "MASK" }, { MULTIPAL, 7, "MULTIPAL" }}; /* Keep all the I/O variables as global to save stack space */ FileInfoRec fiRec; /* GetFileInfo record */ RefNumRecGS clRec; /* CloseGS record */ OpenRecGS opRec; /* OpenGS record */ IORecGS rdRec; /* ReadGS record */ SetPositionRecGS spRec; /* SetMarkGS record */ PositionRecGS psRec; /* GetMarkGS record */ GSString255 fileStr; BlockHeader blockHeader; /* Current APF Block header */ /* Forward function declarations */ Handle readMainBlock( LocInfo *locInfo, Word *colorTable ); /* Define this to turn on debugging */ /* #define DEBUG 1 */ Word parseHeaderKind(void) { int i; for ( i = 0; i < NUM_TYPES; i++ ) { if ( blockTypes[i].len != blockHeader.length ) continue; if ( memcmp( blockTypes[i].kind, blockHeader.kind, blockHeader.length ) == 0 ) return blockTypes[i].type; } return UNKNOWN_KIND; } Handle LoadAPF( char *filename, LocInfo *locInfo, Word *colorTable ) { Handle imageHndl = NULL; /* Handle to the image */ /* Convert the filename to a p-string */ fileStr.length = strlen( filename ); strcpy( fileStr.text, filename ); /* Only open APF files (Filetype $C0, auxtype $0002) */ fiRec.pCount = 4; fiRec.pathname = &fileStr; GetFileInfoGS(&fiRec); /* If we cannot get the file information, fail */ if (toolerror()) return NULL; /* Verify the filetype and auxtype match */ if ( fiRec.fileType != 0xC0 || fiRec.auxType != 0x0002 ) return NULL; /* We've verified it is a APF file, open the file */ opRec.pCount = 12; opRec.pathname = &fileStr; opRec.requestAccess = 1; opRec.resourceNumber = 0; opRec.optionList = NULL; OpenGS(&opRec); if (toolerror()) return NULL; /* Fill in part of other records with data from the open record */ clRec.pCount = 1; clRec.refNum = opRec.refNum; /* These parameters remain the same in the inner loop */ spRec.pCount = 3; spRec.refNum = opRec.refNum; spRec.base = startPlus; spRec.displacement = 0; rdRec.pCount = 4; rdRec.refNum = opRec.refNum; psRec.pCount = 2; psRec.refNum = opRec.refNum; /* Read in block headers until we find the MAIN block or EOF */ for ( ;; ) { /* Get the current position */ GetMarkGS( &psRec ); if ( toolerror() ) break; /* If we've moved to the end of the file, exit */ if ( psRec.position >= opRec.eof ) break; /* First read the block header length and string length */ rdRec.dataBuffer = (Pointer) &blockHeader; rdRec.requestCount = 5; ReadGS(&rdRec); if (toolerror()) break; /* Read in the string type */ rdRec.dataBuffer = (Pointer) &(blockHeader.kind); rdRec.requestCount = blockHeader.length; ReadGS(&rdRec); if (toolerror()) break; /* Dispatch based on the block type */ switch ( parseHeaderKind() ) { case MAIN: imageHndl = readMainBlock( locInfo, colorTable ); break; case UNKNOWN_KIND: break; } /* Move to the next block */ spRec.displacement += blockHeader.blockLen; SetMarkGS( &spRec ); if (toolerror()) break; } CloseGS(&clRec); /* close the file */ return imageHndl; /* return the handle */ } Handle readMainBlock( LocInfo *locInfo, Word *colorTable ) { DirEntry *dirEntry; Word numColorTables; Word numScanLines; Word pixelsPerByte; Word pixelsPerScanLine; Pointer packBytesBuffer; /* Buffer to read file while decompressing */ Word i, len, size; Long imageSize; Pointer imagePtr; Handle imageHndl; /* Found a MAIN block, so read in the master SCB mode word */ rdRec.dataBuffer = (Pointer) &(locInfo->portSCB); rdRec.requestCount = 2; ReadGS(&rdRec); /* If the high byte is not equal to zero, this is unsupported */ if ( (locInfo->portSCB & 0xFF00) != 0 ) return NULL; /* Otherwise the number of pixelsPerByte is 2 or 4 */ if (( locInfo->portSCB & mode640 ) == mode640 ) pixelsPerByte = 4; else pixelsPerByte = 2; /* Number of pixels per scan line */ rdRec.dataBuffer = (Pointer) &pixelsPerScanLine; rdRec.requestCount = 2; ReadGS(&rdRec); /** * We calculate the bytes per scanline for the locInfo rec. Note that * QuickDraw requires that width be a multiple of eight, which we do * not enforce here */ locInfo->width = pixelsPerScanLine / pixelsPerByte; /* Read in the number of color tables. */ rdRec.dataBuffer = (Pointer) &numColorTables; rdRec.requestCount = 2; ReadGS(&rdRec); /** * If there is at least one color table and the arg is non-null, read * it into the buffer */ if ( numColorTables > 0 && colorTable != NULL ) { rdRec.dataBuffer = (Pointer) colorTable; rdRec.requestCount = 32; ReadGS(&rdRec); numColorTables--; } /* Skip the rest of the color tables */ spRec.displacement = 32 * numColorTables; spRec.base = markPlus; SetMarkGS( &spRec ); /* Read in the number of scan lines */ rdRec.dataBuffer = (Pointer) &numScanLines; rdRec.requestCount = 2; ReadGS(&rdRec); /* Read in the directory entries */ dirEntry = (DirEntry *) malloc( numScanLines * sizeof( DirEntry )); if ( dirEntry == NULL ) return NULL; rdRec.dataBuffer = (Pointer) dirEntry; rdRec.requestCount = numScanLines * sizeof( DirEntry ); ReadGS(&rdRec); /* This only supports images <64K */ imageSize = numScanLines * locInfo->width; if ( imageSize > 0xFFFF ) goto size_error; /* Finish filling in the LocInfo structure */ locInfo->boundsRect.v1 = 0; locInfo->boundsRect.h1 = 0; locInfo->boundsRect.v2 = numScanLines; locInfo->boundsRect.h2 = pixelsPerScanLine; /** * Based on the number of lines, the width and SCB mode, we can * calculate how much space we need and allocate a locked handle */ imageHndl = NewHandle( imageSize, userid(), 0x8014, 0 ); if ( toolerror() ) goto alloc_error; /* Finish filling in the locInfo record */ imagePtr = *imageHndl; size = imageSize & 0xFFFF; locInfo->ptrToPixImage = imagePtr; /* Allocate a temporary buffer for reading in the packed data */ packBytesBuffer = (Pointer) malloc( locInfo->width + 1 ); if ( packBytesBuffer == NULL ) goto alloc_error; /** * Decompress each line. The size of the compressed data is given * in the DirEntry, so that can be loaded all at once. */ for ( i = 0; i < numScanLines; i++ ) { /* Read in the number of compressed bytes for this scan line */ rdRec.dataBuffer = packBytesBuffer; rdRec.requestCount = dirEntry[i].unpackLen; ReadGS(&rdRec); if ( toolerror() ) break; /* Unpack them into the allocated buffer */ len = UnPackBytes( packBytesBuffer, dirEntry[i].unpackLen, &imagePtr, &size ); /** * This should always we done in one pass, but continue as long * as we make progress. If zero bytes are unpacked, this is * probably an error, so break. */ if ( len == 0 || len != dirEntry[i].unpackLen ) break; } /* Free the directory entry array and packed buffer */ free( packBytesBuffer ); alloc_error: size_error: free( dirEntry ); /* Return the image memory handle */ return imageHndl; }