Redline/source/macfileio.cpp

397 lines
12 KiB
C++
Raw Normal View History

//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!");
}