397 lines
12 KiB
C++
397 lines
12 KiB
C++
|
//macfileio.cpp
|
||
|
//mac-specific file handling code.
|
||
|
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "fileio.h"
|
||
|
#include "gamemem.h"
|
||
|
#include "error.h"
|
||
|
#include "config.h"
|
||
|
#include "screen.h"
|
||
|
|
||
|
extern "C"{
|
||
|
#include "compress.h"
|
||
|
}
|
||
|
|
||
|
#define kFileNotPackaged -1
|
||
|
|
||
|
//System specific unique file locator
|
||
|
typedef struct{
|
||
|
SInt16 vRefNum;
|
||
|
SInt16 unused;
|
||
|
SInt32 dirID;
|
||
|
Str255 pName;
|
||
|
unsigned int packageOffset,packageSize;
|
||
|
} tFileSystemLocator;
|
||
|
|
||
|
//fills an FSSpecPtr with the file system information for
|
||
|
//the host application's Bundle directory
|
||
|
OSErr GetApplicationBundleFSSpec(FSSpecPtr theFSSpecPtr)
|
||
|
{
|
||
|
FSRef location;
|
||
|
CFBundleRef refMainBundle = NULL;
|
||
|
CFURLRef refMainBundleURL = NULL;
|
||
|
refMainBundle = CFBundleGetMainBundle();
|
||
|
refMainBundleURL = CFBundleCopyBundleURL (refMainBundle);
|
||
|
CFURLGetFSRef(refMainBundleURL,&location);
|
||
|
|
||
|
return FSGetCatalogInfo(&location,kFSCatInfoNone,NULL,NULL,theFSSpecPtr,NULL);
|
||
|
}
|
||
|
|
||
|
typedef struct{
|
||
|
unsigned int offset,unPackedSize,packedSize;
|
||
|
char name[256];
|
||
|
}tPackFileHeaderEntry;
|
||
|
|
||
|
typedef struct{
|
||
|
char magic[8];
|
||
|
int numEntries;
|
||
|
int replaceFlag,unused2,unused3;
|
||
|
tPackFileHeaderEntry entries[];
|
||
|
}tPackFileHeader;
|
||
|
|
||
|
#define kMagicString "R3Dl1n3"
|
||
|
|
||
|
tFileRef SimpleFileGetReference(char *name,tFileTableEntry *fileTable,int fileTableSize)
|
||
|
{
|
||
|
for(int i=0;i<fileTableSize;i++)
|
||
|
if(_stricmp(fileTable[i].name,name)==0)
|
||
|
return i;
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
void IteratePackage(SInt16 vRefNum,SInt32 dirID,Str255 name,tFileTableEntry *fileTable,int *fileTableSize,int maxSize)
|
||
|
{
|
||
|
short ref;
|
||
|
HandleError(HOpenDF(vRefNum,dirID,name,fsCurPerm,&ref));
|
||
|
|
||
|
long headerSize=sizeof(tPackFileHeader);
|
||
|
|
||
|
long eof;
|
||
|
HandleError(GetEOF(ref,&eof));
|
||
|
if(eof<headerSize)
|
||
|
return;
|
||
|
|
||
|
tPackFileHeader *header=(tPackFileHeader*)NewPtr(headerSize);
|
||
|
HandleError(FSRead(ref,&headerSize,header));
|
||
|
if(_stricmp(header->magic,kMagicString))
|
||
|
return;
|
||
|
int numEntries=EndianU32_BtoN(header->numEntries);
|
||
|
DisposePtr((Ptr)header);
|
||
|
|
||
|
HandleError(SetFPos(ref,fsFromStart,0));
|
||
|
|
||
|
headerSize=sizeof(tPackFileHeader)+sizeof(tPackFileHeaderEntry)*numEntries;
|
||
|
header=(tPackFileHeader*)NewPtr(headerSize);
|
||
|
HandleError(FSRead(ref,&headerSize,header));
|
||
|
|
||
|
for(int i=0;i<numEntries;i++)
|
||
|
if(*fileTableSize<maxSize)//is there still space in the file table?
|
||
|
{
|
||
|
int exists=SimpleFileGetReference(header->entries[i].name,fileTable,*fileTableSize);
|
||
|
int replaceable=false;
|
||
|
if(exists)
|
||
|
{
|
||
|
char *extension=FileGetExtension(i);
|
||
|
if(extension)
|
||
|
replaceable=(_stricmp(extension,kFileTypeCarDefinition)&&_stricmp(extension,kFileTypeMapDefinition));
|
||
|
else
|
||
|
replaceable=true;
|
||
|
}
|
||
|
if(exists==-1||(header->replaceFlag&&replaceable))
|
||
|
{
|
||
|
tFileRef ref;
|
||
|
if(exists==-1)
|
||
|
ref=(*fileTableSize)++;
|
||
|
else
|
||
|
ref=exists;
|
||
|
//add file record to file reference table
|
||
|
fileTable[ref].loaded=false;
|
||
|
fileTable[ref].parsed=false;
|
||
|
fileTable[ref].size=EndianU32_BtoN(header->entries[i].unPackedSize);
|
||
|
((tFileSystemLocator*)&(fileTable[ref].fsLoc))->vRefNum=vRefNum;
|
||
|
((tFileSystemLocator*)&(fileTable[ref].fsLoc))->dirID=dirID;
|
||
|
BlockMove(name,((tFileSystemLocator*)&(fileTable[ref].fsLoc))->pName,name[0]+1);
|
||
|
((tFileSystemLocator*)&(fileTable[ref].fsLoc))->packageOffset=EndianU32_BtoN(header->entries[i].offset);
|
||
|
((tFileSystemLocator*)&(fileTable[ref].fsLoc))->packageSize=EndianU32_BtoN(header->entries[i].packedSize);
|
||
|
strcpy(fileTable[ref].name,header->entries[i].name);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ShowAlert("Too many files in Plug-ins folder.","Please remove some files from your Plug-ins folder, and restart Redline.");
|
||
|
ExitToShell();
|
||
|
}
|
||
|
DisposePtr((Ptr)header);
|
||
|
HandleError(FSClose(ref));
|
||
|
}
|
||
|
|
||
|
void AddFile(SInt16 vRefNum,SInt32 dirID,const Str255 name,tFileTableEntry *fileTable,int *fileTableSize,int maxSize)
|
||
|
{
|
||
|
char cName[256];
|
||
|
CopyPascalStringToC(name,cName);
|
||
|
if(*fileTableSize<maxSize)//is there still space in the file table?
|
||
|
if(SimpleFileGetReference(cName,fileTable,*fileTableSize)==-1)
|
||
|
{
|
||
|
//add file record to file reference table
|
||
|
fileTable[*fileTableSize].loaded=false;
|
||
|
fileTable[*fileTableSize].parsed=false;
|
||
|
fileTable[*fileTableSize].tagged=false;
|
||
|
((tFileSystemLocator*)&(fileTable[*fileTableSize].fsLoc))->vRefNum=vRefNum;
|
||
|
((tFileSystemLocator*)&(fileTable[*fileTableSize].fsLoc))->dirID=dirID;
|
||
|
BlockMove(name,((tFileSystemLocator*)&(fileTable[*fileTableSize].fsLoc))->pName,name[0]+1);
|
||
|
((tFileSystemLocator*)&(fileTable[*fileTableSize].fsLoc))->packageOffset=kFileNotPackaged;
|
||
|
strcpy(fileTable[*fileTableSize].name,cName);
|
||
|
(*fileTableSize)++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// if(_stricmp(cName,".DS_Store"))
|
||
|
// PrintConsoleString("File duplicate!: %s\n",cName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ShowAlert("Too many files in Plug-ins folder.","Please remove some files from your Plug-ins folder, and restart Redline.");
|
||
|
ExitToShell();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define IsPackage(i) (_stricmp(cName+strlen(cName)-strlen(kFileTypePackageFile),kFileTypePackageFile)==0)
|
||
|
//recursivly add all files in a directory and subdirectories
|
||
|
//to the file reference table
|
||
|
void IterateDirectoryLevel(SInt16 vRefNum,SInt32 dirID,tFileTableEntry *fileTable,int *fileTableSize,int maxSize)
|
||
|
{
|
||
|
CInfoPBRec cinfo;
|
||
|
Str255 name;
|
||
|
for(int i=1; ;i++)
|
||
|
{
|
||
|
//Get info about the next file in the folder
|
||
|
cinfo.hFileInfo.ioVRefNum = vRefNum;
|
||
|
cinfo.hFileInfo.ioDirID = dirID;
|
||
|
cinfo.hFileInfo.ioNamePtr = name;
|
||
|
cinfo.hFileInfo.ioFDirIndex = i;
|
||
|
OSErr error=PBGetCatInfoSync(&cinfo);
|
||
|
|
||
|
char cName[256];
|
||
|
CopyPascalStringToC(name,cName);
|
||
|
|
||
|
//no more files?
|
||
|
if(error==fnfErr)break;
|
||
|
HandleError(error);
|
||
|
|
||
|
//is this a directory?
|
||
|
if(cinfo.hFileInfo.ioFlAttrib&kioFlAttribDirMask)
|
||
|
//recursively process subdirectory
|
||
|
IterateDirectoryLevel(vRefNum,cinfo.dirInfo.ioDrDirID,fileTable,fileTableSize,maxSize);
|
||
|
|
||
|
#ifndef __PACKER
|
||
|
else if(IsPackage(cName))
|
||
|
IteratePackage(vRefNum,dirID,name,fileTable,fileTableSize,maxSize);
|
||
|
#endif
|
||
|
else
|
||
|
AddFile(vRefNum,dirID,name,fileTable,fileTableSize,maxSize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//declared in fileio.cpp
|
||
|
int FileTableCompare(const void *a,const void *b);
|
||
|
|
||
|
SInt32 GetDirID(FSSpec *spec)
|
||
|
{
|
||
|
CInfoPBRec cinfo;
|
||
|
cinfo.hFileInfo.ioVRefNum = spec->vRefNum;
|
||
|
cinfo.hFileInfo.ioDirID = spec->parID;
|
||
|
cinfo.hFileInfo.ioNamePtr = spec->name;
|
||
|
cinfo.hFileInfo.ioFDirIndex = 0;
|
||
|
OSErr error=PBGetCatInfoSync(&cinfo);
|
||
|
return cinfo.dirInfo.ioDrDirID;
|
||
|
}
|
||
|
|
||
|
#define kPlugInDirName "Plug-Ins"
|
||
|
#define kPlugInDirPName "\pPlug-Ins"
|
||
|
//Initialize file reference table
|
||
|
void FileInitFileTable(tFileTableEntry *fileTable,int maxSize,int *fileTableSize,int reInit)
|
||
|
{
|
||
|
FSSpec spec;
|
||
|
OSErr err;
|
||
|
|
||
|
SInt16 vRefNum; //application's vRefNum
|
||
|
SInt32 dirID; //application's directory ID
|
||
|
|
||
|
if(!reInit)
|
||
|
*fileTableSize=0;
|
||
|
|
||
|
#ifdef __TARGET_PACKER
|
||
|
spec=packFolderFSS;
|
||
|
#else
|
||
|
#ifdef __TARGET_TOOLAPP
|
||
|
err=FSMakeFSSpec(0,0,"\p::::Redline.app",&spec);
|
||
|
if(err==fnfErr)
|
||
|
{
|
||
|
err=FSMakeFSSpec(0,0,"\p:::::Redline.app",&spec);
|
||
|
if(err==fnfErr)
|
||
|
{
|
||
|
ShowAlert("Can't find Redline","Please run the Tool Chest apps from the same folder Redline is in, or from the Tool Chest folder inside the Redline folder.");
|
||
|
ExitToShell();
|
||
|
}
|
||
|
}
|
||
|
#else
|
||
|
//get FSSpec for application's Bundle
|
||
|
GetApplicationBundleFSSpec(&spec);
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
//process file's in application bundle
|
||
|
//and subdirectories into file reference table
|
||
|
IterateDirectoryLevel(spec.vRefNum,GetDirID(&spec),fileTable,fileTableSize,maxSize);
|
||
|
|
||
|
#ifndef __TARGET_PACKER
|
||
|
//process Plug-In folder if it exists
|
||
|
err=FSMakeFSSpec(spec.vRefNum,spec.parID,kPlugInDirPName,&spec);
|
||
|
if(err!=fnfErr)
|
||
|
IterateDirectoryLevel(spec.vRefNum,GetDirID(&spec),fileTable,fileTableSize,maxSize);
|
||
|
|
||
|
//get reference to Preferences folder
|
||
|
SInt16 prefVRefNum;
|
||
|
SInt32 prefDirID;
|
||
|
HandleError(FindFolder(kOnAppropriateDisk,kPreferencesFolderType,kCreateFolder,&prefVRefNum,&prefDirID));
|
||
|
|
||
|
//see if Redline folder exists
|
||
|
/* FSSpec configFolderSpec;
|
||
|
err=FSMakeFSSpec(prefVRefNum,prefDirID,kConfigDirPName,&configFolderSpec);
|
||
|
SInt32 configFolderDirID;
|
||
|
//if not create it
|
||
|
if(err==fnfErr)
|
||
|
HandleError(FSpDirCreate(&configFolderSpec,smSystemScript,&configFolderDirID));
|
||
|
//otherwise get the dirID
|
||
|
else
|
||
|
configFolderDirID=GetDirID(&configFolderSpec);
|
||
|
*/
|
||
|
//see if config file exists
|
||
|
FSSpec configSpec;
|
||
|
err=FSMakeFSSpec(prefVRefNum,prefDirID,kConfigFilePName,&configSpec);
|
||
|
//if not create it
|
||
|
if(err==fnfErr)
|
||
|
FSpCreate(&configSpec,'????','????',smSystemScript);
|
||
|
|
||
|
//Add Config folder to file table
|
||
|
AddFile(prefVRefNum,prefDirID,kConfigFilePName,fileTable,fileTableSize,maxSize);
|
||
|
#endif
|
||
|
|
||
|
//and sort the filetable by file names
|
||
|
if(!reInit)
|
||
|
qsort(fileTable,*fileTableSize,sizeof(tFileTableEntry),&FileTableCompare);
|
||
|
|
||
|
printf("%d files.\n",*fileTableSize);
|
||
|
|
||
|
#ifndef __TARGET_PACKER
|
||
|
if(!FileGetSize(FileGetReference(kConfigFileName)))
|
||
|
{
|
||
|
tFileRef defaults=FileGetReference(kConfigDefault16Name);
|
||
|
int vram=GetVRAMSize();
|
||
|
if(vram>16*1024*1024)
|
||
|
defaults=FileGetReference(kConfigDefault32Name);
|
||
|
if(vram>32*1024*1024)
|
||
|
defaults=FileGetReference(kConfigDefault64Name);
|
||
|
FileSetData(FileGetReference(kConfigFileName),FileGetDataPtr(defaults));
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
//load a file's data
|
||
|
int FileLoadData(tFileRef fileRef)
|
||
|
{
|
||
|
short ref;
|
||
|
HandleError(HOpenDF(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->vRefNum
|
||
|
,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->dirID
|
||
|
,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->pName,fsCurPerm,&ref));
|
||
|
long eof;
|
||
|
HandleError(GetEOF(ref,&eof));
|
||
|
|
||
|
if(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset==kFileNotPackaged)
|
||
|
{
|
||
|
gFileTable[fileRef].data=NewPtr(eof);
|
||
|
gFileTable[fileRef].size=eof;
|
||
|
HandleError(FSRead(ref,&eof,gFileTable[fileRef].data));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
long packedSize=((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageSize;
|
||
|
Ptr packedData=NewPtr(packedSize);
|
||
|
gFileTable[fileRef].data=NewPtr(gFileTable[fileRef].size);
|
||
|
unsigned long offset=((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset;
|
||
|
HandleError(SetFPos(ref,fsFromStart,offset));
|
||
|
HandleError(FSRead(ref,&packedSize,packedData));
|
||
|
|
||
|
|
||
|
compress_identity *iden;
|
||
|
xcompress(COMPRESS_ACTION_IDENTITY,0,0,0,0,0,(ULONG*)&iden);
|
||
|
Ptr workBuffer=NewPtr(iden->memory);
|
||
|
|
||
|
|
||
|
UInt32 ignore;
|
||
|
xcompress(COMPRESS_ACTION_DECOMPRESS,(UBYTE*)workBuffer,(UBYTE*)packedData,packedSize,gFileTable[fileRef].size,(UBYTE*)gFileTable[fileRef].data,(ULONG*)&ignore);
|
||
|
|
||
|
DisposePtr(workBuffer);
|
||
|
DisposePtr(packedData);
|
||
|
}
|
||
|
|
||
|
HandleError(FSClose(ref));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void *FileGetPartialDataPtr(tFileRef fileRef,int offset,int size)
|
||
|
{
|
||
|
if(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset==kFileNotPackaged)
|
||
|
{
|
||
|
short ref;
|
||
|
HandleError(HOpenDF(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->vRefNum
|
||
|
,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->dirID
|
||
|
,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->pName,fsCurPerm,&ref));
|
||
|
void *data=NewPtr(size);
|
||
|
HandleError(SetFPos(ref,fsFromStart,offset));
|
||
|
long readSize=size;
|
||
|
HandleError(FSRead(ref,&readSize,data));
|
||
|
HandleError(FSClose(ref));
|
||
|
return data;
|
||
|
}
|
||
|
else
|
||
|
FailWithErrorString("Cannot get partial data from packaged file.");
|
||
|
}
|
||
|
|
||
|
//write a file's data
|
||
|
void FileStoreData(tFileRef fileRef)
|
||
|
{
|
||
|
if(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset==kFileNotPackaged)
|
||
|
{
|
||
|
short ref;
|
||
|
HandleError(HOpenDF(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->vRefNum
|
||
|
,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->dirID
|
||
|
,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->pName,fsCurPerm,&ref));
|
||
|
long eof=gFileTable[fileRef].size;
|
||
|
HandleError(SetEOF(ref,eof));
|
||
|
HandleError(FSWrite(ref,&eof,gFileTable[fileRef].data));
|
||
|
HandleError(FSClose(ref));
|
||
|
}
|
||
|
else
|
||
|
FailWithErrorString("Cannot write to package file!");
|
||
|
}
|
||
|
|
||
|
void FileAppendData(tFileRef fileRef,void *data,int size)
|
||
|
{
|
||
|
if(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->packageOffset==kFileNotPackaged)
|
||
|
{
|
||
|
short ref;
|
||
|
HandleError(HOpenDF(((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->vRefNum
|
||
|
,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->dirID
|
||
|
,((tFileSystemLocator*)&(gFileTable[fileRef].fsLoc))->pName,fsCurPerm,&ref));
|
||
|
long writeSize=size;
|
||
|
HandleError(SetFPos(ref,fsFromLEOF,0));
|
||
|
HandleError(FSWrite(ref,&writeSize,data));
|
||
|
HandleError(FSClose(ref));
|
||
|
}
|
||
|
else
|
||
|
FailWithErrorString("Cannot write to package file!");
|
||
|
}
|