/*	XGFile.cpp
 *
 *		File I/O that reads and writes data to a real honest to goodness
 *	file. This uses the raw data I/O routines, which are unbuffered on
 *	Windows and some flavors of Unix. (Meaning for speed, use large block
 *	I/O if possible)
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#include <XConfig.h>
#include <XError.h>
#include <XStream.h>
#include <XStringUtil.h>
#include <XWindow.h>
#include <XFile.h>
#include <XError.h>
#include <XPopup.h>
#include <XList.h>
#include <XDialog.h>
#include <XTextControls.h>
#include <XMessages.h>
#include <string.h>

#if OPT_XWIN
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#endif

/************************************************************************/
/*																		*/
/*	File I/O Routines													*/
/*																		*/
/************************************************************************/

/*$	XGFile::XGFile
 *
 *		Create a file object but unopened. You must open the file by
 *	using one of the Open or Create routines before using.
 */

XGFile::XGFile()
{
	fCreate = false;
	fRWFlag = false;
	
#if OPT_MACOS || OPT_XWIN
	fRefNum = -1;
#endif

#if OPT_WINOS
	fRefNum = INVALID_HANDLE_VALUE;
#endif
}

/*$	XGFile::~XGFile
 *
 *		Dispose of this object, closing the file if it's open.
 */

XGFile::~XGFile()
{
	if (IsOpen()) Close();
}

/************************************************************************/
/*																		*/
/*	File Open/Close														*/
/*																		*/
/************************************************************************/

/*$	XGFile::GetFileName
 *
 *		Get the file name of the opened file. This returns only the name
 *	of the file itself, without directory information.
 */

void XGFile::GetFileName(char *out) const
{
	fFileSpec.GetName(out);
}

/*$	XGFile::GetFileExtension
 *
 *		Get the file extension. This returns only the part of the file 
 *	name after the trailing '.'.
 */

void XGFile::GetFileExtension(char *out) const
{
	const char *a;
	const char *b;
	char name[256];
	
	fFileSpec.GetName(name);
	a = NULL;
	for (b = name; *b; b++) {
		if (*b == '.') a = b+1;
	}
	if (a) strcpy(out,a);
	else *out = 0;
}

/*$	XGFile::IsOpen
 *
 *		Is this file currently open?
 */

bool XGFile::IsOpen() const
{
#if OPT_MACOS || OPT_XWIN
	return (fRefNum != -1);
#endif

#if OPT_WINOS
	return (fRefNum != INVALID_HANDLE_VALUE);
#endif
}

/*$	XGFile::Open
 *
 *		Open the file given it's file specifier object. The 'rwflag'
 *	indicates if the file is to be opened read only. If set, the file
 *	is opened read/write; if clear, read only.
 */

#if OPT_MACOS
void XGFile::Open(XGFileSpecifier *file, bool rwflag)
{
	OSErr err;
	FInfo finfo;
	extern bool _GHasFSSpec;
	FSSpec fspec;
	char name[256];
	
	fspec = file->fFileName;
	fRWFlag = rwflag;
	fCreate = false;
	fFileSpec = *file;
	fFileSpec.GetName(name);
	
	if (_GHasFSSpec) {
		err = FSpOpenDF(&fspec,rwflag ? fsRdWrPerm : fsRdPerm,&fRefNum);
	} else {
		err = HOpen(fspec.vRefNum,
					fspec.parID,
					fspec.name,
					rwflag ? fsRdWrPerm : fsRdPerm,
					&fRefNum);
	}
	
	if (err) {
		fRefNum = -1;				// clear so we don't accidently close
		throw XGPostMacOSError(err,KXGFileOpenError,name);
	}

	try {
		if (_GHasFSSpec) {
			err = FSpGetFInfo(&fspec,&finfo);
		} else {
			err = HGetFInfo(fspec.vRefNum,
					  		fspec.parID,
					  		fspec.name,
					  		&finfo);
		}
		
		if (err) throw XGPostMacOSError(err,KXGFileOpenError,name);
		fType = finfo.fdType;
		fCreator = finfo.fdCreator;
		
		/* Call initialize--if it barfed, close file and throw */
		Initialize();
	}
	catch (...) {
		InternalClose();
		throw;
	}
}
#endif

#if OPT_WINOS
void XGFile::Open(XGFileSpecifier *file, bool rwflag)
{
	const char *f;
	fRWFlag = rwflag;
	fCreate = false;
	char name[256];
	
	f = file->fFileName.Get();
	fFileSpec = *file;
	fFileSpec.GetName(name);
	
	fRefNum = CreateFile(f,
						 rwflag ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ,
						 0,
						 NULL,
						 OPEN_EXISTING,
						 FILE_ATTRIBUTE_NORMAL,
						 0);
	if (fRefNum == INVALID_HANDLE_VALUE) {
		throw XGPostWinOSError(GetLastError(),KXGFileOpenError,name);
	}

	/* Call initialize--if it barfed, close file and throw */
	try {
		Initialize();
	}
	catch (...) {
		InternalClose();
		throw;
	}
}
#endif

#if OPT_XWIN
void XGFile::Open(XGFileSpecifier *file, bool rwflag)
{
	const char *f;
	fRWFlag = rwflag;
	fCreate = false;
	char name[256];
	
	f = file->fFileName.Get();
	fFileSpec = *file;
	fFileSpec.GetName(name);
	fRefNum = ::open(f,rwflag ? O_RDWR : O_RDONLY);
	if (fRefNum == -1) {
		throw XGPostUnixOSError(errno,KXGFileOpenError,name);
	}

	/* Call initialize--if it barfed, close file and throw */
	try {
		Initialize();
	}
	catch (...) {
		InternalClose();
		throw;
	}
}
#endif

/*$	XGFile::Create
 *
 *		Create this file and open it. The Macintosh version takes a
 *	creator and type specifier. The file is created and opened read/write.
 */

#if OPT_MACOS
void XGFile::Create(XGFileSpecifier *file, OSType creator, OSType type)
{
	FSSpec fspec;
	OSErr err;
	FInfo finfo;
	extern bool _GHasFSSpec;
	char name[256];
	
	fspec = file->fFileName;
	fCreate = true;
	fRWFlag = true;
	fFileSpec = *file;
	fFileSpec.GetName(name);
	
	if (_GHasFSSpec) {
		err = ::FSpCreate(&fspec,creator,type,smSystemScript);
	} else {
		err = ::HCreate(fspec.vRefNum,
					    fspec.parID,
					    fspec.name,
					    creator,
					    type);
	}
	if (err) {
		if (err == dupFNErr) {
			/*
			 *	File already exists.
			 */
			
			if (_GHasFSSpec) {
				err = ::FSpOpenDF(&fspec,fsRdWrPerm,&fRefNum);
			} else {
				err = ::HOpen(fspec.vRefNum,
							  fspec.parID,
							  fspec.name,
							  fsRdWrPerm,
							  &fRefNum);
			}
			if (err) {
				fRefNum = -1;
				throw XGPostMacOSError(err,KXGFileCreateError,name);
			}
			
			if (_GHasFSSpec) {
				::FSpGetFInfo(&fspec,&finfo);
			} else {
				::HGetFInfo(fspec.vRefNum,
							fspec.parID,
							fspec.name,
							&finfo);
			}
			finfo.fdType = type;
			finfo.fdCreator = creator;
			if (_GHasFSSpec) {
				::FSpSetFInfo(&fspec,&finfo);
			} else {
				::HSetFInfo(fspec.vRefNum,
							fspec.parID,
							fspec.name,
							&finfo);
			}
			
			err = ::SetEOF(fRefNum,0L);
			if (err) {
				FSClose(fRefNum);
				fRefNum = -1;
				throw XGPostMacOSError(err,KXGFileCreateError,name);
			}
		} else {
			throw XGPostMacOSError(err,KXGFileCreateError,name);
		}
	} else {
		if (_GHasFSSpec) {
			err = ::FSpOpenDF(&fspec,fsRdWrPerm,&fRefNum);
		} else {
			err = ::HOpen(fspec.vRefNum,
						  fspec.parID,
						  fspec.name,
						  fsRdWrPerm,
						  &fRefNum);
		}
		if (err) {
			fRefNum = -1;
			throw XGPostMacOSError(err,KXGFileCreateError,name);
		}
	}
	
	fCreator = creator;
	fType = type;
	
	/* Call initialize--if it barfed, close file and throw */
	try {
		Initialize();
	}
	catch (...) {
		InternalClose();
		throw;
	}
}
#endif

#if OPT_WINOS
void XGFile::Create(XGFileSpecifier *file)
{
	const char *f;
	char name[256];

	fCreate = true;
	fRWFlag = true;
	
	f = file->fFileName.Get();
	fFileSpec = *file;
	fFileSpec.GetName(name);
	fRefNum = CreateFile(f,
						 GENERIC_READ | GENERIC_WRITE,
						 0,
						 NULL,
						 OPEN_ALWAYS,
						 FILE_ATTRIBUTE_NORMAL,
						 0);
	if (fRefNum == INVALID_HANDLE_VALUE) {
		throw XGPostWinOSError(GetLastError(),KXGFileOpenError,name);
	}

	/* Call initialize--if it barfed, close file and throw */
	try {
		Initialize();
	}
	catch (...) {
		InternalClose();
		throw;
	}
}
#endif

#if OPT_XWIN
void XGFile::Create(XGFileSpecifier *file)
{
	const char *f;
	char name[256];

	fCreate = true;
	fRWFlag = true;
	
	f = file->fFileName.Get();
	fFileSpec = *file;
	fFileSpec.GetName(name);
	fRefNum = ::open(f,O_RDWR | O_CREAT);
	if (fRefNum == -1) {
		throw XGPostUnixOSError(errno,KXGFileOpenError,name);
	}

	/* Call initialize--if it barfed, close file and throw */
	try {
		Initialize();
	}
	catch (...) {
		InternalClose();
		throw;
	}
}
#endif

/*$	XGFile::Close
 *
 *		Close the file if it's open. This first calls the 'Terminate'
 *	virtual override, giving whatever overrode this a chance to terminate
 *	what it's doing. The file is closed regardless if an event is thrown.
 */

void XGFile::Close()
{
	try {
		Terminate();
	}
	catch (...) {
		InternalClose();
		throw;
	}
	InternalClose();
}

/*	XGFile::InternalClose
 *
 *		Internal routine that actually performs the close operation
 */

void XGFile::InternalClose()
{
	if (IsOpen()) {
#if OPT_MACOS
		FSClose(fRefNum);
		fRefNum = -1;
#endif

#if OPT_WINOS
		CloseHandle(fRefNum);
		fRefNum = INVALID_HANDLE_VALUE;
#endif

#if OPT_XWIN
		close(fRefNum);
		fRefNum = -1;
#endif
	}
}

/*$	XGFile::Initialize
 *
 *		This is called after the file is opened or created. This allows
 *	me to impose higher level structure on my file, such as if the
 *	file was a database engine.
 */

void XGFile::Initialize()
{
}

/*$	XGFile::Terminate
 *
 *		Terminate; this is called just before the file is closed.
 */

void XGFile::Terminate()
{
}

/************************************************************************/
/*																		*/
/*	Read/Write															*/
/*																		*/
/************************************************************************/

/*$	XGFile::Read
 *
 *		Read data from the file. This returns the number of bytes
 *	actually read, or throws an exception if there was a problem.
 */

long XGFile::Read(long len, void *data)
{
	char name[256];

	Assert(IsOpen());
	
#if OPT_MACOS
	OSErr err;
	
	err = FSRead(fRefNum,&len,(Ptr)data);
	if (err && (err != eofErr)) {
		fFileSpec.GetName(name);
		throw XGPostMacOSError(err,KXGFileReadError,name);
	}
	return len;
#endif

#if OPT_WINOS
	DWORD dw;
	
	if (!ReadFile(fRefNum,data,len,&dw,NULL)) {
		fFileSpec.GetName(name);
		throw XGPostWinOSError(GetLastError(),KXGFileReadError,name);
	}
	return dw;
#endif

#if OPT_XWIN
	long rnum;
	
	rnum = ::read(fRefNum,data,len);
	if (rnum == -1) {
		fFileSpec.GetName(name);
		throw XGPostUnixOSError(errno,KXGFileReadError,name);
	}
	return rnum;
#endif
}

/*$	XGFile::Write
 *
 *		Write data to the file. This returns the number of bytes
 *	written; normally that is equal to the size of the output buffer.
 *	If a problem occured, this throws an exception.
 */

long XGFile::Write(long len, void *data)
{
	char name[256];
	
	Assert(IsOpen());
	Assert(fRWFlag);
	
#if OPT_MACOS
	OSErr err;
	
	err = FSWrite(fRefNum,&len,(Ptr)data);
	if (err) {
		fFileSpec.GetName(name);
		throw XGPostMacOSError(err,KXGFileReadError,name);
	}
	return len;
#endif

#if OPT_WINOS
	DWORD dw;
	
	if (!WriteFile(fRefNum,data,len,&dw,NULL)) {
		fFileSpec.GetName(name);
		throw XGPostWinOSError(GetLastError(),KXGFileReadError,name);
	}
	return dw;
#endif

#if OPT_XWIN
	long rnum;
	
	rnum = ::write(fRefNum,data,len);
	if (rnum == -1) {
		fFileSpec.GetName(name);
		throw XGPostUnixOSError(errno,KXGFileReadError,name);
	}
	return rnum;
#endif
}

/*$	XGFile::Flush
 *
 *		Flush the data that was just written. This makes sure the data
 *	is written out to the file.
 */

void XGFile::Flush()
{
	Assert(IsOpen());

#if OPT_MACOS
	ParamBlockRec pb;
	
	pb.ioParam.ioCompletion = NULL;
	pb.ioParam.ioRefNum = fRefNum;
	Assert(noErr == PBFlushFile(&pb,false));
#endif

#if OPT_WINOS
	Assert(FlushFileBuffers(fRefNum));
#endif

#if OPT_XWIN
	/* No low level Unix call to flush a buffer. Even fflush() on */
	/* the Linux StdC library does nothing... */
#endif
}

/*$	XGFile::GetEOF
 *
 *		Get the end of file marker position. This basically returns the
 *	size of this file.
 */

long XGFile::GetEOF()
{
	Assert(IsOpen());
	
#if OPT_MACOS
	OSErr err;
	long eof;
	
	err = ::GetEOF(fRefNum,&eof);
	Assert(err == noErr);
	return eof;
#endif

#if OPT_WINOS
	DWORD len;
	
	len = ::GetFileSize(fRefNum,NULL);
	Assert(len != 0xFFFFFFFF);
	return len;
#endif

#if OPT_XWIN
	struct stat buf;
	
	fstat(fRefNum,&buf);
	return buf.st_size;
#endif
}

/*$	XGFile::SetEOF
 *
 *		Set the file size
 */

void XGFile::SetEOF(long len)
{
	char name[256];

	Assert(IsOpen());
	Assert(fRWFlag);

#if OPT_XWIN
	long fpos;
	
	if (len < GetFilePos()) SetFilePos(len);
	if (ftruncate(fRefNum,len)) {
		fFileSpec.GetName(name);
		throw XGPostUnixOSError(errno,KXGFileSetEOFError,name);
	}
#endif

#if OPT_MACOS
	OSErr err;
	
	err = ::SetEOF(fRefNum,len);
	if (err) {
		fFileSpec.GetName(name);
		throw XGPostMacOSError(err,KXGFileSetEOFError,name);
	}
#endif

#if OPT_WINOS
	long fpos;
	BOOL err;
		
	fpos = GetFilePos();
	SetFilePos(len);
	err = ::SetEndOfFile(fRefNum);
	if (fpos > len) fpos = len;
	SetFilePos(fpos);
	if (!err) {
		fFileSpec.GetName(name);
		throw XGPostWinOSError(GetLastError(),KXGFileSetEOFError,name);
	}
#endif
}

/*$	XGFile::GetFilePos
 *
 *		Get the current read/write file position.
 */

long XGFile::GetFilePos()
{
	Assert(IsOpen());

#if OPT_MACOS
	OSErr err;
	long pos;
	
	err = ::GetFPos(fRefNum,&pos);
	Assert(err == noErr);
	return pos;
#endif

#if OPT_WINOS
	DWORD pos;
	
	pos = ::SetFilePointer(fRefNum,0,NULL,FILE_CURRENT);
	//Assert(pos == 0xFFFFFFFFUL);
	Assert(pos != INVALID_SET_FILE_POINTER);
	return pos;
#endif

#if OPT_XWIN
	long pos;
	
	pos = lseek(fRefNum,0,SEEK_CUR);
	Assert(pos != -1);
	return pos;
#endif
}

/*$	XGFile::SetFilePos
 *
 *		Set the file position.  This should be called
 *	between a group of reads and a group of writes in order to make
 *	sure things get syncronized.
 */

void XGFile::SetFilePos(long pos)
{
	char name[256];
	
	Assert(IsOpen());

#if OPT_MACOS
	OSErr err;
	
	err = ::SetFPos(fRefNum,fsFromStart,pos);
	if (err != noErr) {
		fFileSpec.GetName(name);
		throw XGPostMacOSError(err,KXGFileSetPosError,name);
	}
#endif

#if OPT_WINOS
	DWORD p;
	
	p = ::SetFilePointer(fRefNum,pos,NULL,FILE_BEGIN);
	if (p == 0xFFFFFFFFUL) {
		fFileSpec.GetName(name);
		throw XGPostWinOSError(GetLastError(),KXGFileSetPosError,name);
	}
#endif

#if OPT_XWIN
	long p;
	
	p = lseek(fRefNum,pos,SEEK_SET);
	if (p == -1) {
		fFileSpec.GetName(name);
		throw XGPostUnixOSError(errno,KXGFileSetPosError,name);
	}
#endif
}

/************************************************************************/
/*																		*/
/*	File Dialogs (Unix)													*/
/*																		*/
/************************************************************************/

#if OPT_XWIN

/*
 *	Globals
 */

static char			*GExtension;
static char			*GFilter;
static bool			GSaveFlag;			// save file (new file name) dialog?
static XGStringList	*GList;

/*
 *	Built-in dialogs
 */

static unsigned char GOpenDialogData[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, /* 00000000: .......` */
    0x00, 0x00, 0x00, 0x50, 0x64, 0x6C, 0x6F, 0x67, /* 00000008: ...Pdlog */
    0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, /* 00000010: ........ */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000018: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000020: ........ */
    0x00, 0x00, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, /* 00000028: .....H.. */
    0x00, 0xEC, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, /* 00000030: .....H.. */
    0x00, 0xEC, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, /* 00000038: .....H.. */
    0x00, 0xEC, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, /* 00000040: .....H.. */
    0x00, 0xEC, 0x09, 0x4F, 0x70, 0x65, 0x6E, 0x20, /* 00000048: ...Open. */
    0x46, 0x69, 0x6C, 0x65, 0x00, 0x00, 0x00, 0x00, /* 00000050: File.... */
    0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x65, /* 00000058: ...d...e */
    0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, /* 00000060: ...:.... */
    0x00, 0x00, 0x00, 0x2A, 0x70, 0x6F, 0x70, 0x6D, /* 00000068: ...*popm */
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, /* 00000070: ........ */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 00000078: ........ */
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, /* 00000080: ........ */
    0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x24, /* 00000088: .......$ */
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 00000090: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, /* 00000098: .....D.. */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x6C, 0x69, /* 000000A0: .....4li */
    0x73, 0x74, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 000000A8: st...... */
    0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 000000B0: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, /* 000000B8: ........ */
    0x00, 0x28, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, /* 000000C0: .(...... */
    0x00, 0xC8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 000000C8: ........ */
    0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 000000D0: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000000D8: ........ */
    0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000000E0: .@...... */
    0x00, 0x30, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, /* 000000E8: .0text.. */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, /* 000000F0: ........ */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000000F8: ........ */
    0x00, 0x10, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, /* 00000100: ........ */
    0x00, 0x40, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, /* 00000108: .@...... */
    0xFF, 0xFF, 0x00, 0x00, 0x05, 0x26, 0x4F, 0x70, /* 00000110: .....&Op */
    0x65, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000118: en...... */
    0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000120: .R...... */
    0x00, 0x42, 0x65, 0x64, 0x69, 0x74, 0x00, 0x00, /* 00000128: .Bedit.. */
    0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, /* 00000130: ........ */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000138: ........ */
    0x00, 0x48, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, /* 00000140: .H...... */
    0x00, 0xEC, 0x00, 0x00, 0x00, 0xE3, 0xFF, 0xFF, /* 00000148: ........ */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 00000150: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000158: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000160: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000168: ........ */
    0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, /* 00000170: ...D.... */
    0x00, 0x00, 0x00, 0x34, 0x70, 0x62, 0x74, 0x6E, /* 00000178: ...4pbtn */
    0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, /* 00000180: ...d.... */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 00000188: ........ */
    0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0C, /* 00000190: ........ */
    0x00, 0x00, 0x01, 0x3E, 0x00, 0x00, 0x00, 0x28, /* 00000198: ...>...( */
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 000001A0: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x4F, /* 000001A8: .......O */
    0x70, 0x65, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x46, /* 000001B0: pen....F */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, /* 000001B8: .......6 */
    0x70, 0x62, 0x74, 0x6E, 0x00, 0x00, 0x00, 0x65, /* 000001C0: pbtn...e */
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, /* 000001C8: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, /* 000001D0: ........ */
    0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x01, 0x3E, /* 000001D8: ...,...> */
    0x00, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, /* 000001E0: ...H.... */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000001E8: ........ */
    0x00, 0x00, 0x06, 0x43, 0x61, 0x6E, 0x63, 0x65, /* 000001F0: ...Cance */
    0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000001F8: l....... */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x70, 0x62, /* 00000200: .....4pb */
    0x74, 0x6E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, /* 00000208: tn...... */
    0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 00000210: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, /* 00000218: ........ */
    0x00, 0x58, 0x00, 0x00, 0x01, 0x3C, 0x00, 0x00, /* 00000220: .X...<.. */
    0x00, 0x74, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 00000228: .t...... */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000230: ........ */
    0x04, 0x48, 0x6F, 0x6D, 0x65, 0x00              /* 00000238: .Home.   */
}; /* _R56526573_0085 */

static unsigned char GSaveDialogData[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, /* 00000000: .......` */
    0x00, 0x00, 0x00, 0x50, 0x64, 0x6C, 0x6F, 0x67, /* 00000008: ...Pdlog */
    0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, /* 00000010: ........ */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000018: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000020: ........ */
    0x00, 0x00, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, /* 00000028: .....H.. */
    0x00, 0xEC, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, /* 00000030: .....H.. */
    0x00, 0xEC, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, /* 00000038: .....H.. */
    0x00, 0xEC, 0x00, 0x00, 0x01, 0x48, 0x00, 0x00, /* 00000040: .....H.. */
    0x00, 0xEC, 0x09, 0x53, 0x61, 0x76, 0x65, 0x20, /* 00000048: ...Save. */
    0x46, 0x69, 0x6C, 0x65, 0x00, 0x00, 0x00, 0x00, /* 00000050: File.... */
    0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x65, /* 00000058: ...d...e */
    0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, /* 00000060: ...:.... */
    0x00, 0x00, 0x00, 0x2A, 0x70, 0x6F, 0x70, 0x6D, /* 00000068: ...*popm */
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, /* 00000070: ........ */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 00000078: ........ */
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, /* 00000080: ........ */
    0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x24, /* 00000088: .......$ */
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 00000090: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, /* 00000098: .....D.. */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x6C, 0x69, /* 000000A0: .....4li */
    0x73, 0x74, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 000000A8: st...... */
    0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 000000B0: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, /* 000000B8: ........ */
    0x00, 0x28, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, /* 000000C0: .(...... */
    0x00, 0xC8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 000000C8: ........ */
    0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 000000D0: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000000D8: ........ */
    0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000000E0: .@...... */
    0x00, 0x30, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, /* 000000E8: .0text.. */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, /* 000000F0: ........ */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000000F8: ........ */
    0x00, 0x10, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, /* 00000100: ........ */
    0x00, 0x40, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0xFF, /* 00000108: .@...... */
    0xFF, 0xFF, 0x00, 0x00, 0x05, 0x26, 0x53, 0x61, /* 00000110: .....&Sa */
    0x76, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000118: ve...... */
    0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000120: .R...... */
    0x00, 0x42, 0x65, 0x64, 0x69, 0x74, 0x00, 0x00, /* 00000128: .Bedit.. */
    0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, /* 00000130: ........ */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000138: ........ */
    0x00, 0x48, 0x00, 0x00, 0x00, 0xCD, 0x00, 0x00, /* 00000140: .H...... */
    0x00, 0xEC, 0x00, 0x00, 0x00, 0xE3, 0xFF, 0xFF, /* 00000148: ........ */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 00000150: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000158: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000160: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000168: ........ */
    0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, /* 00000170: ...D.... */
    0x00, 0x00, 0x00, 0x34, 0x70, 0x62, 0x74, 0x6E, /* 00000178: ...4pbtn */
    0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, /* 00000180: ...d.... */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 00000188: ........ */
    0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x0C, /* 00000190: ........ */
    0x00, 0x00, 0x01, 0x3E, 0x00, 0x00, 0x00, 0x28, /* 00000198: ...>...( */
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 000001A0: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x53, /* 000001A8: .......S */
    0x61, 0x76, 0x65, 0x00, 0x00, 0x00, 0x00, 0x46, /* 000001B0: ave....F */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, /* 000001B8: .......6 */
    0x70, 0x62, 0x74, 0x6E, 0x00, 0x00, 0x00, 0x65, /* 000001C0: pbtn...e */
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, /* 000001C8: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, /* 000001D0: ........ */
    0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x01, 0x3E, /* 000001D8: ...,...> */
    0x00, 0x00, 0x00, 0x48, 0xFF, 0xFF, 0xFF, 0xFF, /* 000001E0: ...H.... */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000001E8: ........ */
    0x00, 0x00, 0x06, 0x43, 0x61, 0x6E, 0x63, 0x65, /* 000001F0: ...Cance */
    0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000001F8: l....... */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x70, 0x62, /* 00000200: .....4pb */
    0x74, 0x6E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, /* 00000208: tn...... */
    0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 00000210: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, /* 00000218: ........ */
    0x00, 0x58, 0x00, 0x00, 0x01, 0x3C, 0x00, 0x00, /* 00000220: .X...<.. */
    0x00, 0x74, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 00000228: .t...... */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000230: ........ */
    0x04, 0x48, 0x6F, 0x6D, 0x65, 0x00              /* 00000238: .Home.   */
}; /* _R56526573_0086 */

/*	SortList
 *
 *		Do a single sort for the qsort object. 
 */

static int SortList(const void *a, const void *b)
{
	short aa = *(short *)a;
	short bb = *(short *)b;
	char buf1[256];
	char buf2[256];
	int i;
	
	GList->GetString(aa,buf1);
	GList->GetString(bb,buf2);
	i = strcmp(buf1,buf2);
	if (i < 0) return -1;
	if (i > 0) return 1;
	if (aa < bb) return -1;
	if (aa > bb) return 1;
	return 0;
}

/*	LoadCWD
 *
 *		Load the current working directory into the popup menu control
 */
 
static void LoadCWD(XGPopControl *pop)
{
	char buffer[512];
	char *a,*b;
	int count;
	
	/*
	 *	Get the current working directory
	 */
	
	getcwd(buffer,512);
	
	/*
	 *	Clear the popup menu
	 */
	
	count = 0;
	pop->DeleteAll();
	pop->Insert(0,"/");					// root directory
	a = buffer+1;
	while (*a) {
		b = buffer;
		while ((*a != '/') && (*a != 0)) *b++ = *a++;
		if (*a) a++;
		*b = 0;
		pop->Insert(++count,buffer);
		if (*a == 0) break;
	}
	pop->SetValue(count);
}

/*	FilterName
 *
 *		Filter the file name. Return true if this should be skipped.
 */

static bool FilterName(char *name)
{
	char *x,*y,*a,*b;
	
	if (GFilter == NULL) return false;

	y = NULL;
	for (x = name; *x; x++) {
		if (*x == '.') y = x+1;
	}
	if (!y) y = x;
	
	x = GFilter;
	while (*x) {
		for (a = y, b = x; *a; a++, b++) {
			if (*a != *b) break;
		}
		if ((*a == 0) && ((*b == ',') || (*b == 0))) return false;
		
		while ((*x) && (*x != ',')) x++;
		if (*x == 0) break;
		x++;
	}
	return true;
}

/*	LoadDir
 *
 *		Load the contents of the directory into the drop list
 */

static bool LoadDir(XGListControl *list)
{
	XGStringList slist;
	char buffer[512];
	DIR *dir;
	struct dirent *file;
	struct stat filestat;
	short *lsort;
	int i;
	int len;
	
	GList = &slist;
	
	/*
	 *	Open directory and load list
	 */
	
	getcwd(buffer,512);
	dir = opendir(buffer);
	if (dir == NULL) return false;
	
	while (NULL != (file = readdir(dir))) {
		strcpy(buffer,file->d_name);
		if (!strcmp(buffer,".") || !strcmp(buffer,"..")) continue;
		stat(buffer,&filestat);
		if (S_ISDIR(filestat.st_mode)) {
			strcat(buffer,"/");
		} else if (FilterName(buffer)) continue;
		
		slist.Append(buffer);
	}
	closedir(dir);
	
	/*
	 *	Empty list?
	 */
	
	len = (int)slist.GetNumStrings();
	if (len == 0) {
		list->DeleteAll();
		return true;
	}
	
	/*
	 *	Sort the list
	 */
	
	lsort = (short *)malloc(sizeof(short) * len);
	for (i = 0; i < len; i++) lsort[i] = i;
	qsort((void *)lsort,len,sizeof(short),SortList);
	
	/*
	 *	Load the list
	 */
	
	list->SetUpdate(false);
	list->DeleteAll();
	for (i = 0; i < len; i++) {
		slist.GetString(lsort[i],buffer);
		list->Insert(i,buffer);
	}
	list->SetUpdate(true);
	
	free((void *)lsort);
}

/*	SetExtension
 *
 *		Set the file extension if it doesn't match the default
 *	extension
 */

static void SetExtension(char *c)
{
	char *x,*y;
	
	if (GExtension == NULL) return;
	
	y = NULL;
	for (x = c; *x; x++) {
		if (*x == '.') y = x+1;
	}
	if (y) {
		if (!strcmp(y,GExtension)) return;
	}
	strcat(c,".");
	strcat(c,GExtension);
}

/*	DialogProc
 *
 *		This is the proc to handle the open and save dialogs.
 */

static long DialogProc(XGDialog *dlog, void *data, long msg, long arg, void *parg)
{
	XGListControl *c;
	XGEditText *e;
	char buffer[512];
	long sel;
	char *end;
	struct stat status;
	XGPopControl *ctrl;
	char str[256];
	long i,len;
	
	switch (msg) {
		case KXGDialogMessage:
			if (arg == KXGDMInitDialog) {
				/*
				 *	Load the contents of this thing
				 */
				
				LoadCWD(dynamic_cast<XGPopControl *>(dlog->FindViewByID(1)));
				LoadDir(dynamic_cast<XGListControl *>(dlog->FindViewByID(2)));

				e = dynamic_cast<XGEditText *>(dlog->FindViewByID(3));
				if (GSaveFlag) e->SetTitle((char *)data);
				else e->SetTitle("");
				break;
			}
			return -1;
			
		case KXGListSelect:
			/*
			 *	First, emulate the list value select
			 */
			
			if (-1 == DialogProc(dlog,data,KXGListValue,2,NULL)) return -1;
			dlog->GetText(3,sizeof(buffer),buffer);
			if (buffer[0]) {
				/*
				 *	Second, emulate the 'ok' click
				 */
				
				return DialogProc(dlog,data,KXGControlClick,100,NULL);
			}
			break;
						
		case KXGListValue:
			c = dynamic_cast<XGListControl *>(dlog->FindViewByID(2));
			if (c) {
				sel = c->GetValue();
				if (sel == -1) {
					if (GSaveFlag) strcpy(buffer,(char *)data);
					else buffer[0] = 0;
				}
				else c->GetItem(sel,buffer);
				e = dynamic_cast<XGEditText *>(dlog->FindViewByID(3));
				for (end = buffer; *end; end++) {
					if (*end == '/') {
						*end = 0;
						break;
					}
				}
				e->SetTitle(buffer);
			}
			return 0;
			
		case KXGPopValue:
			ctrl = dynamic_cast<XGPopControl *>(dlog->FindViewByID(1));
			if (ctrl) {
				/*
				 *	Figure out how far up to go
				 */
				
				len = ctrl->GetValue();
				if (len == ctrl->Length()-1) return 0;
				strcpy(buffer,"/");
				for (i = 1; i < len; i++) {
					ctrl->GetItem(i,str);
					strcat(buffer,str);
					strcat(buffer,"/");
				}
				ctrl->GetItem(len,str);
				strcat(buffer,str);
				
				/*
				 *	Now set the directory
				 */
				
				if (!chdir(buffer)) {
					LoadCWD(dynamic_cast<XGPopControl *>(dlog->FindViewByID(1)));
					LoadDir(dynamic_cast<XGListControl *>(dlog->FindViewByID(2)));
				}
			}
			return 0;
	
		case KXGControlClick:
			if (arg == 4) {
				/*
				 *	Go to the user's home directory
				 */
				
				if (!chdir(getenv("HOME"))) {
					e = dynamic_cast<XGEditText *>(dlog->FindViewByID(3));
					LoadCWD(dynamic_cast<XGPopControl *>(dlog->FindViewByID(1)));
					LoadDir(dynamic_cast<XGListControl *>(dlog->FindViewByID(2)));
					if (GSaveFlag) e->SetTitle((char *)data);
					else e->SetTitle("");
				}
				return 0;
			}
			if (arg == 101) {
				dlog->EndDialog(0);
				return 0;
			}
			if (arg == 100) {
				/*
				 *	Is this a directory?
				 */
				
				e = dynamic_cast<XGEditText *>(dlog->FindViewByID(3));
				e->GetTitle(buffer);
				
				if (GSaveFlag) {
					/*
					 *	Add the extension to the save only if the file
					 *	does not exist, or if it's not a directory. If it's
					 *	a directory, we're changing directories. If it's
					 *	a file that exists, we're replacing it...
					 */
					
					if (-1 == stat(buffer,&status)) {
						SetExtension(buffer);
					}
					if (!S_ISDIR(status.st_mode)) {
						SetExtension(buffer);
					}
				}
				
				if (-1 == stat(buffer,&status)) {

					/*
					 *	Does not exist.
					 */
					
					if (!GSaveFlag) {
						Error(KXGFileNoExistError,buffer);
						return 0;
					} else {
						strcpy((char *)data,buffer);
						dlog->EndDialog(1);
						return 0;
					}
				} else {
					/*
					 *	Exists
					 */
					
					if (S_ISDIR(status.st_mode)) {
						/*
						 *	Directory. Change dir down
						 */
						
						if (!chdir(buffer)) {
							LoadCWD(dynamic_cast<XGPopControl *>(dlog->FindViewByID(1)));
							LoadDir(dynamic_cast<XGListControl *>(dlog->FindViewByID(2)));
							if (GSaveFlag) e->SetTitle((char *)data);
							else e->SetTitle("");
						}
						return 0;
					}
					
					/*
					 *	File.
					 */
					
					if (GSaveFlag) {
						if (Caution(KXGFileExistError,buffer)) return 0;
					}
					strcpy((char *)data,buffer);
					dlog->EndDialog(1);
					return 0;
				}
			}
				
			return -1;
			
	}
	return -1;
}

/*	XGFile::OpenDialog
 *
 *		Open the file by running the file open dialog. This returns NULL
 *	if there was an error or the file open dialog was canceled. The
 *	Macintosh version of this takes as it's arguments a 'paramResID',
 *	which refers to an 'Open' resource which specifies the file types
 *	that can be opened.
 *
 *		On Windows, the param string is a string giving the file types
 *	which can be opened. The parameters should be of the format:
 *
 *			(description) '|' *.ext '|' (description) '|' *.ext ...
 *
 *		The second parameter is the default file extension to be used
 *	if one is not provided in the open dialog box.
 *
 *		On Unix, the param string is a comma-separated list of
 *	extensions which can be opened.
 */

XGFile *XGFile::OpenDialog(char *tlist, bool rwflag)
{
	char fname[256];
	XGFile *f;
	XGFileSpecifier spec;
	
	GSaveFlag = false;
	fname[0] = 0;
	GFilter = tlist;
	if (!XGDialog::ModalDialog(GOpenDialogData,DialogProc,(void *)fname)) {
		return NULL;
	}
	
	f = new XGFile;
	spec = fname;
	f->Open(&spec,rwflag);
	return f;
}

/*	XGFile::SaveDialog
 *
 *		Run a save dialog and create a new file with the specified name.
 *
 *		On the Macintosh, this specifies a resource ID of a 'Save' resource
 *	with the default prompt, the creator and file type specifiers. The creator
 *	and file type specifier is used when the file is created.
 *
 *		On Windows, this specifies the file types, in the same format
 *	string used by OpenDialog. On Unix, this specifies the default extension
 *	to be used, as well as a prompt string.
 */

XGFile *XGFile::SaveDialog(char *ext, char *name)
{
	char fname[256];
	XGFile *f;
	XGFileSpecifier spec;
	
	GSaveFlag = true;
	strcpy(fname,name);
	GFilter = NULL;
	GExtension = ext;
	if (!XGDialog::ModalDialog(GSaveDialogData,DialogProc,(void *)fname)) {
		return NULL;
	}
	
	f = new XGFile;
	spec = fname;
	f->Create(&spec);
	return f;
}

#endif

