//////////
//
//	File:		QTReadWriteJPEG.c
//
//	Contains:	Sample code for compressing and decompressing JPEG images.
//
//	Written by:	Michael Marinkovich and Guillermo Ortiz
//	Revised by:	Tim Monroe
//				Based heavily on the existing "JPEG Sample" code.
//
//	Copyright:	© 1996-1998 by Apple Computer, Inc., all rights reserved.
//
//	Change History (most recent first):
//
//	   <4>	 	02/03/99	rtm		reworked prompt and filename handling to remove "\p" sequences
//	   <3>	 	04/27/98	rtm		revised to uncouple this file from its Macintosh framework,
//									to make the coding style conform to other code samples, and
//									to make it run on Windows
//	   <2>	 	02/??/97	mwm		fixed some memory leaks; expanded JPEG parsing
//	   <1>	 	04/03/96	mwm		initial coding
//	   
//	This sample code illustrates how to compress and decompress JPEG images using QuickTime. 
//	We use the FCompressImage function, but you could also use the CompressImage function.
//	Although this sample demonstrates only JPEG compression/decompression, you could use this
//	as a framework for other types of compression (except for the decoding of the JPEG header).
//
//	In this sample code, we allow the user to open a JPEG image file; then we draw it into
//	a window on the screen. Your application, of course, will probably want to do more
//	interesting things with the image. We also allow the user to save an image using JPEG
//	compression.
//
//	NOTES:
//
//	*** (1) ***
//	You may incorporate this sample code into your applications without restriction, though
//	the sample code has been provided "AS IS" and the responsibility for its operation is 100% yours.
//	However, what you are not permitted to do is to redistribute the source as "DSC Sample Code"
//	after having made changes. If you're going to redistribute the source, we require that you
//	make it clear in the source that the code was descended from Apple Sample Code, but that
//	you've made changes.
//
//////////

//////////
//
// header files
//
//////////

#include "QTReadWriteJPEG.h"
#include "QTUtilities.h"


//////////
//
// global variables
//
//////////

GWorldPtr						gGWorld = NULL;			// the GWorld we load the image data into
WindowPtr						gImageWindow = NULL;	// the window we display the image in


//////////
//
// QTJPEG_PromptUserForJPEGFileAndDisplay
// Let the user select a JPEG image file, then display it in a window.
//
//////////

void QTJPEG_PromptUserForJPEGFileAndDisplay (void)
{
	SFTypeList					myTypeList;
	StandardFileReply			myReply;
	PixMapHandle				myPixMap;	
	Rect						myRect;
	OSErr						myErr = noErr;
	
	// have the user select a JPEG image file
	myTypeList[0] = kQTFileTypeJPEG;

	StandardGetFilePreview(NULL, 1, myTypeList, &myReply);
	if (!myReply.sfGood)
		goto bail;
	
	// read the file data into an offscreen graphics world
	myErr = QTJPEG_ReadJPEG(myReply.sfFile, &gGWorld);
	if (myErr != noErr)
		goto bail;

	myRect = gGWorld->portRect;
	myPixMap = GetGWorldPixMap(gGWorld);
	if (LockPixels(myPixMap)) {

		// create a window to display the image in; then draw into that window		
		MacOffsetRect(&myRect, 50, 50);
		gImageWindow = NewCWindow(NULL, &myRect, myReply.sfFile.name, true, movableDBoxProc, (WindowPtr)-1L, true, 0);
		if (gImageWindow == NULL)
			goto bail;
			
		MacOffsetRect(&myRect, -50, -50);
		
		// copy the image from the offscreen port into the window
		CopyBits(	(BitMapPtr)(*myPixMap),
					(BitMapPtr)(&(gImageWindow->portBits)),
					&myRect, 
					&gImageWindow->portRect,
					srcCopy, 
					NULL);
	}

bail:
	UnlockPixels(myPixMap);
}


//////////
//
// QTJPEG_ReadJPEG
// Open a JPEG file with supplied FSSpec; the graphics world containing the image is returned through theGWorld.
//
//////////

OSErr QTJPEG_ReadJPEG (FSSpec theFile, CGrafPtr *theGWorld)
{
	ImageDescriptionHandle	 	myDesc;
	Handle						myData;
	GWorldPtr					myGWorld = NULL;
	GWorldPtr					oldWorld;
	GDHandle					oldGD;
	PixMapHandle				myPixMap;
	ICMDataProcRecord 			myLoadProc;
	Rect						myRect;
	DLDataRec					myDataRec;
	long						mySize;
	short						myRefNum;
	Ptr							myDataPtr;
	OSErr						myErr = paramErr;
	
	// save the current graphics port
	GetGWorld(&oldWorld, &oldGD);
	
	if (theGWorld != NULL) {
			
		myErr = FSpOpenDF(&theFile, fsRdWrShPerm, &myRefNum);
		if (myErr == noErr) {
		
			myDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
			if (myDesc != NULL) {
				HLock((Handle)myDesc);
				myErr = QTJPEG_ReadJPEGHeader(myRefNum, myDesc, &myRect);
				if (myErr == noErr) {
				
					myData = NewHandleClear(kBufferSize);
					myErr = MemError();
					
					if ((myData != NULL) && (myErr == noErr)) {
						myErr = QTJPEG_NewJPEGWorld(&myGWorld, (*myDesc)->depth, myRect);
						if ((myGWorld != NULL) && (myErr == noErr)){
							myErr = SetFPos(myRefNum, fsFromStart, 0);
							if (myErr == noErr) {
							
								myPixMap = GetGWorldPixMap(myGWorld);
								LockPixels(myPixMap);
								SetGWorld(myGWorld, NULL);
								HLock(myData);
								
								mySize = kBufferSize;
								
								// make sure the file size is greater than the buffer size
								(void)GetEOF(myRefNum, &mySize);
								if (kBufferSize > mySize)
									mySize = mySize;
									
								myErr = FSRead(myRefNum, &mySize, *myData);
								if (myErr == noErr) {
									mySize = (*myDesc)->dataSize - kBufferSize;
									if (mySize < 0) 
										mySize = 0;
										
									myDataRec.fRefNum = myRefNum;
									myDataRec.fFileLength = mySize;
									myDataRec.fOrigPtr = *myData;
									myDataRec.fEndPtr = *myData + kBufferSize;
									myLoadProc.dataProc = NewICMDataProc(QTJPEG_DataLoadingProc);
									myLoadProc.dataRefCon = (long)&myDataRec;
									
									myDataPtr = StripAddress(*myData);
									
									myErr = FDecompressImage(	myDataPtr,
																myDesc,
																myPixMap,
																&myRect,
																NULL,
																srcCopy,
																NULL,
																NULL,
																NULL,
														  		codecHighQuality,
														  		anyCodec,
														  		kBufferSize,
														  		&myLoadProc,
														  		NULL);
														  
									DisposeRoutineDescriptor(myLoadProc.dataProc);
		 							HUnlock(myData);
									UnlockPixels(myPixMap);
									
									// restore the previous graphics world 
									SetGWorld(oldWorld, oldGD);
									
									if (myErr == noErr)
										*theGWorld = myGWorld;
								}
							}
							
							if (myErr != noErr)
								DisposeGWorld(myGWorld);
						}
						
						DisposeHandle(myData);
					}
				}
				
				HUnlock((Handle)myDesc);				
				DisposeHandle((Handle)myDesc);
			}
			
			FSClose(myRefNum);				// close the file
		}
	}
			
	return(myErr);
}


//////////
//
// QTJPEG_ReadJPEGHeader
// Read the JPEG header and fill out the specified ImageDescription with needed info.
//
//////////

OSErr QTJPEG_ReadJPEGHeader (short theRefNum, ImageDescriptionHandle theDesc, Rect *theRect)
{
	long 					mySize;
	short					mySkip;
	UInt8 					myMarker;
	Boolean					isJFIF = false;
	Boolean					readingExtension = false;
	OSErr					myErr = noErr;
	
	// set file position to beginning of file
	myErr = SetFPos(theRefNum, fsFromStart , 0);
	if (myErr != noErr)
		return(myErr);
		
	// get file length, so we don't overflow
	myErr = GetEOF(theRefNum, &mySize);
	if (myErr != noErr)
		return(myErr);
	
	(*theDesc)->dataSize = mySize;

	// loop forever
	while (true) {
		myMarker = QTJPEG_FindNextMarker(theRefNum);
		
		switch (myMarker) {
			case kSOIMarker:
				isJFIF = true;
				break;
				
			case kAPPOMarker + 0:
			case kAPPOMarker + 1:
			case kAPPOMarker + 2:
			case kAPPOMarker + 3:
			case kAPPOMarker + 4:
			case kAPPOMarker + 5:
			case kAPPOMarker + 6:
			case kAPPOMarker + 7:
			case kAPPOMarker + 8:
			case kAPPOMarker + 9:
			case kAPPOMarker + 10:
			case kAPPOMarker + 11:
			case kAPPOMarker + 12:
			case kAPPOMarker + 13:
			case kAPPOMarker + 14:
			case kAPPOMarker + 15:
				myErr = QTJPEG_HandleAPPOMarker(myMarker, theRefNum, theDesc, &readingExtension);
				if (myErr != noErr)
					return(myErr);
				break;
				
			case kCommentMarker:
				QTJPEG_SkipLength(theRefNum);
				break;
		
			case kSOFMarker + 0:		// start of frame header marker
			case kSOFMarker + 1:
			case kSOFMarker + 2:
			case kSOFMarker + 3:
			case kSOFMarker + 5:
			case kSOFMarker + 6:
			case kSOFMarker + 7:
			case kSOFMarker + 9:
			case kSOFMarker + 10:
			case kSOFMarker + 11:
			case kSOFMarker + 13:
			case kSOFMarker + 14:
			case kSOFMarker + 15:
				myErr = QTJPEG_HandleSOFMarker(theRefNum, theDesc, readingExtension);
				if (myErr != noErr)
					return(myErr);
					
				if (!readingExtension) {
					MacSetRect(theRect, 0, 0, (*theDesc)->width, (*theDesc)->height);
					return(noErr);
				}
				break;
				
			case kDACMarker:
				mySkip = QTJPEG_ReadWord(theRefNum) - 2;
				mySkip *= QTJPEG_ReadWord(theRefNum);
				myErr = SetFPos(theRefNum, fsFromMark, mySkip);
				break;
				
			case kSOSMarker:
				QTJPEG_HandleSOSMarker(theRefNum);
				break;
				
			case kDHTMarker:
			case kDQTMarker:
			case kRSTOMarker:
				QTJPEG_SkipLength(theRefNum);
				break;
				
			case kEOIMarker:		// we reached the end of image
				// we are reading an extension so keep going
				if (readingExtension)
					readingExtension = false;
				break;
		}
	}
	
	return(myErr);
}


//////////
//
// QTJPEG_FindNextMarker
// Find the next marker in the specified file.
//
//////////

UInt8 QTJPEG_FindNextMarker (long theRefNum)
{
	UInt8 			myMarker;
	
	myMarker = QTJPEG_ReadByte(theRefNum);
	
	while (myMarker == kStartMarker)
		myMarker = QTJPEG_ReadByte(theRefNum);

	while (myMarker == 0x00) {
		myMarker = QTJPEG_ReadByte(theRefNum);

		while (myMarker != kStartMarker)
			myMarker = QTJPEG_ReadByte(theRefNum);
			
		myMarker = QTJPEG_ReadByte(theRefNum);
	}
		
	return(myMarker);
}


//////////
//
// QTJPEG_HandleAPPOMarker
// Handle an APPO marker in the specified file.
//
//////////

OSErr QTJPEG_HandleAPPOMarker (UInt8 theMarker, long theRefNum, ImageDescriptionHandle theDesc, Boolean *readingExtension)
{
	Fixed			xRes, yRes;
	long 			myLength;
	short			myUnits;
	short			myVersion;
	UInt8			myExtension;
	UInt8 			myType[5];
	OSErr			myErr = noErr;

	// read skip bytes - header length - skip count
	myLength = QTJPEG_ReadWord(theRefNum) - 2;
	
	if ((theMarker == kAPPOMarker) && (myLength >= 14)) {
		myType[0] = QTJPEG_ReadByte(theRefNum);
		myType[1] = QTJPEG_ReadByte(theRefNum);
		myType[2] = QTJPEG_ReadByte(theRefNum);
		myType[3] = QTJPEG_ReadByte(theRefNum);
		myType[4] = QTJPEG_ReadByte(theRefNum);
		
		// check to see if we really have the JFIF header
		if ((myType[0] == 'J') &&
			(myType[1] == 'F') &&
			(myType[2] == 'I') &&
			(myType[3] == 'F')) {
			
	  		myVersion = QTJPEG_ReadWord(theRefNum);

			if (myVersion < 0x100)	
				return(paramErr); 	// don't know this
			else
				(*theDesc)->version = myVersion;
				
			myUnits = QTJPEG_ReadByte(theRefNum);
			xRes = QTJPEG_ReadWord(theRefNum);
	  		yRes = QTJPEG_ReadWord(theRefNum);

			switch (myUnits) {
				case 0:			// no res, just aspect ratio
					xRes = FixMul(72L << 16, xRes << 16);
					yRes = FixMul(72L << 16, yRes << 16);
					break;
					
				case 1:			// dots per inch
					xRes = xRes << 16;
					yRes = yRes << 16;
					break;
					
				case 2:			// dots per centimeter (we convert to dpi )
					xRes = FixMul(0x28a3d, xRes << 16);
					yRes = FixMul(0x28a3d, xRes << 16);		// yRes?? RTM
					break;	
					
				default:
					break;
			}
			
			(*theDesc)->hRes = xRes;
			(*theDesc)->vRes = yRes;

			myLength -= 12;
			myErr = SetFPos(theRefNum, fsFromMark, myLength);
			
		} else {
			if ((myType[0] == 'J') &&
				(myType[1] == 'F') &&
				(myType[2] == 'X') &&
				(myType[3] == 'X')) { 
				
				*readingExtension = true;		// next markers are extensions; ignore them

				myExtension = QTJPEG_ReadByte(theRefNum);
				switch (myExtension) {
					case 0x10:
					case 0x11:
					case 0x13:
						break;
					default:
						return(paramErr);
				}
			}
		}
	} else
		myErr = SetFPos(theRefNum, fsFromMark, myLength);

	return(myErr);
}


//////////
//
// QTJPEG_HandleSOFMarker
// Handle an SOF marker in the specified file.
//
//////////

OSErr QTJPEG_HandleSOFMarker (long theRefNum, ImageDescriptionHandle theDesc, Boolean readingExtension)
{
	short 			myWidth = 0;
	short 			myHeight = 0;
	short 			myComponents;
	short			myLength;
	StringPtr		myTitle = QTUtils_ConvertCToPascalString(kWindowTitle);
	OSErr			myErr = noErr;

	if (!readingExtension) {
		myLength = QTJPEG_ReadWord(theRefNum);
		QTJPEG_ReadByte(theRefNum);
		myHeight = QTJPEG_ReadWord(theRefNum);
		myWidth = QTJPEG_ReadWord(theRefNum);

		// make sure we do have something to display
		if ((myWidth != 0) && (myHeight != 0)) {
		
			// now set up the image description
			(*theDesc)->idSize 			= sizeof(ImageDescription);
			(*theDesc)->cType 			= FOUR_CHAR_CODE('jpeg');
			(*theDesc)->dataRefIndex 	= 0;
			(*theDesc)->revisionLevel 	= 0;
			(*theDesc)->vendor 			= 0;
			(*theDesc)->temporalQuality = 0;
			(*theDesc)->spatialQuality	= codecNormalQuality;
			(*theDesc)->width 			= myWidth;
			(*theDesc)->height 			= myHeight;
			(*theDesc)->frameCount 		= 1;
			BlockMove(myTitle, (*theDesc)->name, 13);
			(*theDesc)->clutID 			= -1;
			
			myComponents = QTJPEG_ReadByte(theRefNum);
			
			switch (myComponents) {
				case 1:		
					(*theDesc)->depth = 40;
					break;

				case 3:
					(*theDesc)->depth = 32;
					break;

				case 4:
					(*theDesc)->depth = 32;
					break;
										
				default:
					myErr = paramErr;
					return(myErr);
					break;
			}
			
			myErr = SetFPos(theRefNum, fsFromMark, myLength - 8);
			return(noErr);
		}
		
	} else {
		myLength = QTJPEG_ReadWord(theRefNum) - 2;
		myErr = SetFPos(theRefNum, fsFromMark, myLength);
		if (myErr != noErr)
			return(myErr);
	}
	
	free(myTitle);

	return(myErr);
}


//////////
//
// QTJPEG_HandleSOSMarker
// Handle an SOS marker in the specified file.
//
//////////

void QTJPEG_HandleSOSMarker (long theRefNum)
{
	short		myComponents;
	short		myWord;
	
	QTJPEG_ReadWord(theRefNum);
	myComponents = QTJPEG_ReadByte(theRefNum);
	
	for (myWord = 0; myWord < myComponents; myWord++)
		QTJPEG_ReadWord(theRefNum);
}


//////////
//
// QTJPEG_ReadByte
// Read the next byte from the specified file.
//
//////////

UInt8 QTJPEG_ReadByte (short theRefNum)
{
	UInt8		myData;
	long		myBytesNeeded = sizeof(char);

	(void)FSRead(theRefNum, &myBytesNeeded, &myData);
	return(myData);
}


//////////
//
// QTJPEG_ReadWord
// Read the next word from the specified file.
//
//////////

UInt16 QTJPEG_ReadWord (short theRefNum)
{
	UInt16		myData;
	long		myBytesNeeded = sizeof(UInt16);

	(void)FSRead(theRefNum, &myBytesNeeded, &myData);
	
	// in JPEG files, the data is stored in a big-endian order
	myData = EndianU16_BtoN(myData);
	return(myData);
}


//////////
//
// QTJPEG_SkipLength
// Skip over the length word.
//
//////////

void QTJPEG_SkipLength (long theRefNum)
{
	UInt16		mySkip;
	
	mySkip = QTJPEG_ReadWord(theRefNum) - 2;
	SetFPos(theRefNum, fsFromMark, mySkip);
}


//////////
//
// QTJPEG_NewJPEGWorld
// Return, through the theWorld parameter, a new offscreen graphics world suitable
// for drawing a JPEG image of the specified bitdepth into.
//
//////////

OSErr QTJPEG_NewJPEGWorld (GWorldPtr *theWorld, short theDepth, Rect theRect)
{
	GWorldPtr		oldPort;
	GDHandle		oldGD;
	PixMapHandle	myPixMap;
	CTabHandle		myCTab = NULL;
	OSErr			myErr = paramErr;
	
	if (theWorld != NULL) {
		// save the current graphics port
		GetGWorld(&oldPort, &oldGD);
	
		// if depth is greater than 32, then the image is grayscale
		if (theDepth > 32) {
			myCTab = GetCTable(theDepth);
			theDepth = theDepth - 32;
		}
		
		// first try to allocate a GWorld in the application's heap
		myErr = NewGWorld(theWorld, theDepth, &theRect, myCTab, NULL, 0L);
		
		// otherwise, try to allocate a GWorld in temporary memory
		if (myErr != noErr)
			myErr = NewGWorld(theWorld, theDepth, &theRect, myCTab, NULL, useTempMem);
	
		if ((myErr == noErr) && (theWorld != NULL))  {
			myPixMap = GetGWorldPixMap(*theWorld);
			if (LockPixels(myPixMap)) {
				SetGWorld(*theWorld, NULL);
				EraseRect(&theRect);
				UnlockPixels(myPixMap);
			}
		}
		
		SetGWorld(oldPort, oldGD);	
	}
	
	return(myErr);
}


//////////
//
// QTJPEG_SaveJPEG
// Save the specified image as a compressed file.
//
//////////

OSErr QTJPEG_SaveJPEG (GWorldPtr theWorld)
{
	StandardFileReply			myReply;
	ImageDescriptionHandle	 	myDesc;
	Handle						myData;
	Rect						myRect;
	PixMapHandle				myPixMap;	
	CTabHandle					myCTab = NULL;
	ICMFlushProcRecord			myFlushProc;	
	short						myRefNum;
	short						myDepth;
	StringPtr 					myImagePrompt = QTUtils_ConvertCToPascalString(kSaveImagePrompt);
	StringPtr 					myImageFileName = QTUtils_ConvertCToPascalString(kSaveImageFileName);
	OSErr						myErr = paramErr;
	
	if (theWorld == NULL)
		goto bail;
	
	// have the user select the name of the new image file
	StandardPutFile(myImagePrompt, myImageFileName, &myReply);
	if (!myReply.sfGood)
		goto bail;
	
	myDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
	if (myDesc == NULL)
		goto bail;
	
	myRect = theWorld->portRect;
	myPixMap = GetGWorldPixMap(theWorld);
	
	if (LockPixels(myPixMap)) {	
	
		// if less than 16-bit then get the color table of our GWorld
		myDepth = (**myPixMap).pixelSize;
		if (myDepth < 16)
			myCTab = (**myPixMap).pmTable;
		
		myData = NewHandle(kBufferSize);
		myErr = MemError();
		
		if ((myData != NULL) && (myErr == noErr)) {
			CodecType			myCodec = kJPEGCodecType;

			HLock(myData);
													 	
			if (myReply.sfReplacing) 
				myErr = FSpDelete(&myReply.sfFile);
		
			myErr = FSpCreate(&myReply.sfFile, kImageFileCreator, kQTFileTypeJPEG, myReply.sfScript);
			
			if (myErr == noErr)
				myErr = FSpOpenDF(&myReply.sfFile, fsRdWrPerm, &myRefNum);
				
			if (myErr == noErr)
				myErr = SetFPos(myRefNum, fsFromStart, 0);
				
			if (myErr == noErr) {
				ICMFlushProcRecordPtr		myFlushProcPtr = NULL;

				myFlushProc.flushProc = NewICMFlushProc(QTJPEG_DataUnloadProc);
				myFlushProc.flushRefCon = myRefNum;
				myFlushProcPtr = &myFlushProc;

				// compress the image
				myErr = FCompressImage(	myPixMap,
										&myRect,
										myDepth,
									 	codecNormalQuality,
									 	myCodec,
										anyCodec,
										myCTab,
										codecFlagWasCompressed,
										kBufferSize,
										myFlushProcPtr,
										NULL,
										myDesc,
										*myData);
				
				if (myErr == noErr)
					myErr = SetFPos(myRefNum, fsFromStart, (**myDesc).dataSize);

				if (myErr == noErr)
					myErr = SetEOF(myRefNum, (**myDesc).dataSize);
							 
				if (myErr == noErr)		
					myErr = FSClose(myRefNum);
				
				HUnlock(myData);
				DisposeHandle(myData);
				
				DisposeRoutineDescriptor(myFlushProc.flushProc);
			}
		}
	}
	
	UnlockPixels(myPixMap);
	
bail:
	if (myDesc != NULL)
		DisposeHandle((Handle)myDesc);
	
	free(myImagePrompt);
	free(myImageFileName);

	return(myErr);
}			
	
	
//////////
//
// QTJPEG_DataUnloadProc
// A data unloading procedure: write the compressed data to disk.
//
// The theRefCon parameter is assumed to be a file reference number of an open file.
//
//////////

PASCAL_RTN OSErr QTJPEG_DataUnloadProc (Ptr theData, long theBytesNeeded, long theRefCon)
{
	OSErr		myErr = noErr;
	
	if (theData == NULL) {
		// if data is NULL, set a new position in the file from the current mark, offset by bytesNeeded
		myErr = SetFPos(theRefCon, fsFromMark, theBytesNeeded);
	} else {
		// otherwise, write the specified data to disk
		myErr = FSWrite(theRefCon, &theBytesNeeded, theData);
	}
	
	return(myErr);
}


//////////
//
// QTJPEG_DataLoadingProc
// A data loading procedure: read the data from disk.
//
// The theRefCon parameter is assumed to be a pointer to our custom data-loading record.
//
//////////

PASCAL_RTN OSErr QTJPEG_DataLoadingProc (Ptr *theData, long theBytesNeeded, long theRefCon)
{
	long		myUnusedDataLen;
	long		myNewDataLen;
	DLDataPtr	myDataRec;
	Ptr			myDataPtr;
	OSErr		myErr = noErr;
	
	myDataRec = (DLDataPtr)theRefCon;
	
	if (theData == NULL) {
		myErr = SetFPos(myDataRec->fRefNum, fsFromMark, theBytesNeeded);
	} else {	
		myDataPtr = *theData;
		
		// if QT requests more data than is in the buffer, we will have to load more
		if ((myDataPtr + theBytesNeeded) >= myDataRec->fEndPtr) {
			// move whats left up to the front of the buffer
			myUnusedDataLen = myDataRec->fEndPtr - myDataPtr;
			BlockMove(myDataPtr, myDataRec->fOrigPtr, myUnusedDataLen);
			
			// now fill the buffer with new data,
			// following the data we moved to the front of the buffer
			myNewDataLen = kBufferSize - myUnusedDataLen;
			
			if (myNewDataLen > myDataRec->fFileLength)
				myNewDataLen = myDataRec->fFileLength;
				
			myDataPtr = myDataRec->fOrigPtr + myUnusedDataLen;
			
			myErr = FSRead(myDataRec->fRefNum, &myNewDataLen, myDataPtr);
			
			myDataRec->fFileLength -= myNewDataLen;

			*theData = myDataRec->fOrigPtr;
		}
	}
	
	return(myErr);
}
