/***************************************************************************** ** ** ** Neversoft Entertainment ** ** ** ** Copyright (C) 1999 - All Rights Reserved ** ** ** ****************************************************************************** ** ** ** Project: System Library ** ** ** ** Module: File IO (File) ** ** ** ** File name: p_filesys.cpp ** ** ** ** Created by: 03/20/00 - mjb ** ** ** ** Description: PS2 File System ** ** ** *****************************************************************************/ /***************************************************************************** ** Includes ** *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PAL #define __DVD_ONLY__ 0 // 0 = DVD or CD, 1 = Just DVD (for copy protection) #else #define __DVD_ONLY__ 0 // 0 = DVD or CD, 1 = Just DVD (for copy protection) #endif #define ASYNC_QUICK_FILESYS 1 #define ASYNC_HOST_FILESYS 0 #define DISABLE_QUICK_FILESYSTEM 0 /***************************************************************************** ** DBG Information ** *****************************************************************************/ namespace File { /***************************************************************************** ** Externals ** *****************************************************************************/ /***************************************************************************** ** Defines ** *****************************************************************************/ #ifndef SECTOR_SIZE #define SECTOR_SIZE 2048 #endif // SECTOR_SIZE // MEMOPT: If we run out of IOP memory again, it is possible that we could use the pcm buffer // (The one that is TOTAL_IOP_BUFFER_SIZE_NEEDED) for the file i/o, since probably music or // streams will not need to be playing when a file is loading. // That would free up 2048*48=98304 bytes!! #define FILESYS_NUM_SECTORS_IN_BUFFER 48 #define FILESYS_STREAM_BUFFER_NUM_PARTITIONS 3 #define FILESYS_IOP_BUFFER_SIZE ( SECTOR_SIZE * FILESYS_NUM_SECTORS_IN_BUFFER ) #define FILESYS_NUM_SECTORS_PER_READ 16 #define FILESYS_CD_READ_SIZE ( SECTOR_SIZE * FILESYS_NUM_SECTORS_PER_READ ) #define IOP_TO_EE_BUFFER_ALLIGNMENT 64 // Static globals: static void *gFilesysIOPStreamBuffer = NULL; //static char *gIOPToEEBuffer; unsigned int gWadLSN = 0; // Made global for p_AsyncFilesystem static bool gQuickFileSystemInitialzed = false; static bool gStreaming = false; /***************************************************************************** ** Private Types ** *****************************************************************************/ #define READBUFFERSIZE (10240*5) // GJ: I added this to track how many skyFiles are active // at any given time. I am assuming that there's only one, // in which case I can just create one global instance // (previously, we were allocating it off the current heap, // which was causing me grief during the CAS loading process) //int g_fileOpenCount = 0; struct skyFile { // the following must match the dumbSkyFile struct // in pre.cpp, or else pre file i/o won't work properly int gdfs; int32 POS; int32 SOF; // the rest is skyFile-specific uint8 readBuffer[READBUFFERSIZE]; uint32 bufferPos; bool bufferValid; bool locked; // whether the skyfile is currently in use // Used when CD uint32 WadOffset; const char *pFilename; // Used when non-CD #define MAX_PFILESYS_NAME_SIZE 255 char filename[ MAX_PFILESYS_NAME_SIZE ]; #if ASYNC_HOST_FILESYS CAsyncFileHandle *p_async_file; #endif }; // At first, I assumed that only one skyfile could be open at // a time, but that's not true when testing streams from the // PC build. // pc builds need to have two (one for normal files, and one for streams) // GJ: for THPS4, we increased this to 3, so that we can play multiple streams const int MAXOPENFILES = 3; // Ken note: It used to be that MAXOPENFILES was defined to be 1 when __NOPT_CDROM__OLD // was set. Now that __NOPT_CDROM__OLD is not used anymore and CD() is used at runtime // instead, I made MAXOPENFILES always 2. Then, where MAXOPENFILES used to be used in the code // it now uses a var set to 1 or 2 depending on CD() static skyFile g_skyFile[MAXOPENFILES]; skyFile* lock_skyfile( void ) { // cd builds can only have one file open at any given time int max_open_files=Config::CD() ? 1:MAXOPENFILES; int i; // this tries to find an unlocked skyfile for ( i = 0; i < max_open_files; i++ ) { if ( !g_skyFile[i].locked ) { g_skyFile[i].locked = true; return &g_skyFile[i]; } } if (!Config::CD()) { Dbg_Message( "Here are the files currently open:" ); for ( i = 0; i < max_open_files; i++ ) { Dbg_Message( "%s", g_skyFile[ i ].filename ); } } // if we get here, that means that all the sky files were locked Dbg_MsgAssert( 0, ( "Trying to open too many files simultaneously (max=%d)", max_open_files ) ); return NULL; } void unlock_skyfile( skyFile* pSkyFile ) { // cd builds can only have one file open at any given time int max_open_files=Config::CD() ? 1:MAXOPENFILES; // this tries to find the sky file that the caller is referencing for ( int i = 0; i < max_open_files; i++ ) { if ( pSkyFile == &g_skyFile[i] ) { Dbg_MsgAssert( g_skyFile[i].locked, ( "Trying to unlock a sky file too many times" ) ); g_skyFile[i].locked = false; return; } } // if we get here, that means that the pointer didn't match one of the valid skyfiles Dbg_MsgAssert( 0, ( "Unrecognized sky file" ) ); } /***************************************************************************** ** Private Data ** *****************************************************************************/ static int32 s_open_files = 0; // The header file (SKATE4.hed) used for looking up where files are within // SKATE4.wad SHed *gpHed=NULL; // Made global for p_AsyncFilesystem static int WadId=-1; /***************************************************************************** ** Public Data ** *****************************************************************************/ /***************************************************************************** ** Private Prototypes ** *****************************************************************************/ /***************************************************************************** ** Private Functions ** *****************************************************************************/ /******************************************************************/ /* skyTransMode */ /* */ /* Attempt to convert a mode string to an open mode */ /* */ /* On entry : access mode */ /* On exit : integer mode */ /* */ /******************************************************************/ static int skyTransMode(const char *access) { int mode; char *r; char *w; char *a; char *plus; char *n; char *d; /* I add a couple of new characters for now: n non-blocking mode d no write back d cache */ r = strrchr(access, (int)'r'); w = strrchr(access, (int)'w'); a = strrchr(access, (int)'a'); plus = strrchr(access, (int)'+'); n = strrchr(access, (int)'n'); d = strrchr(access, (int)'d'); if (plus) mode = SCE_RDWR; else if (r) mode = SCE_RDONLY; else if (w) mode = SCE_WRONLY; else if (a) mode = SCE_WRONLY; else return(0); /* later we will test for SCE_CREAT & !SCE_TRUNC as a seek to end of file */ if (w) mode |= SCE_CREAT | SCE_TRUNC; if (a) mode |= SCE_CREAT; if (n) mode |= SCE_NOWAIT; if (d) Dbg_MsgAssert( 0,( "Hmm... gotta find out what SCE_NOWBDC was in previous library." )); // mode |= SCE_NOWBDC; return(mode); } void StartStreaming( uint32 lsn ) { Dbg_MsgAssert( gFilesysIOPStreamBuffer,( "IOP stream buffer not initialized" )); sceCdSync(0); // Make sure there aren't any non-blocking CD reads happening Pcm::StopStreams( ); // Garrett: I found streamed sounds can skip when using the quick load // if ( Pcm::UsingCD( ) ) // { // Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." )); // } sceCdRMode mode; mode.trycount = 255; mode.spindlctrl = SCECdSpinNom; mode.datapattern = SCECdSecS2048; mode.pad = 0; while ( !( gStreaming = sceCdStStart( lsn, &mode ) ) ) { printf( "aah ha! would have caused the crash!!!\n" ); sceCdStop(); sceCdSync(0); } } void StopStreaming( void ) { if (!Config::CD()) { return; } if ( gStreaming ) { while ( !sceCdStStop( ) ) { printf( "sceCdStStop failed\n" ); } } gStreaming = false; } uint32 CanFileBeLoadedQuickly( const char *filename ) { #if DISABLE_QUICK_FILESYSTEM return 0; #endif if (!Config::CD()) { return 0; } if ( !gQuickFileSystemInitialzed ) { return ( 0 ); } if ( !gpHed ) { return ( 0 ); } SHedFile *tempHed = FindFileInHed( filename, gpHed ); if ( tempHed ) return ( tempHed->GetFileSize() ); return ( 0 ); } #if ASYNC_QUICK_FILESYS == 0 static void HandleCDStreamingError( uint32 lsn ) { int err; err = sceCdGetError( ); printf( "CD Error %d\n", err ); // let's just stop everything, and try again fresh and new: sceCdBreak( ); sceCdSync( 0 ); StartStreaming( lsn ); /* switch ( err ) { case ( SCECdErNO ): break; case ( SCECdErTRMOPN ): break; case ( ): break; case ( ): break; case ( ): break; default: break; }*/ } #endif // ASYNC_QUICK_FILESYS == 0 bool LoadFileQuicklyPlease( const char *filename, uint8 *addr ) { if (!Config::CD()) { return false; } if ( !gQuickFileSystemInitialzed ) { return ( false ); } #if ASYNC_QUICK_FILESYS CAsyncFileHandle *p_handle = CAsyncFileLoader::sOpen(filename, false); if (p_handle) { p_handle->Load(addr); p_handle->WaitForIO(); CAsyncFileLoader::sClose(p_handle); } else { Dbg_MsgAssert(0, ("Didn't get handle pointer from async filesys for file %s", filename)); return false; } #else Dbg_MsgAssert( gFilesysIOPStreamBuffer,( "IOP stream buffer not initialized" )); if ( !gpHed ) { return ( false ); } SHedFile *tempHed = FindFileInHed( filename, gpHed ); if ( !tempHed ) { Dbg_MsgAssert( 0,( "File cannot be loaded quickly." )); return ( false ); } uint32 bytesRequired = tempHed->GetFileSize(); // uint32 offsetWithinBuffer = tempHed->Offset & ( SECTOR_SIZE - 1 ); if ( tempHed->Offset & ( SECTOR_SIZE - 1 ) ) { int i; for ( i = 0; i != -1; i++ ) { printf( "fix wad so all files start on 2048 boundary.\n" ); } } if (CAsyncFileLoader::sAsyncInUse()) { CAsyncFileLoader::sWaitForIOEvent(true); Dbg_Message("************************ Can't do a normal read when async filesystem is in use"); //Dbg_MsgAssert(0, ("Can't do a normal read when async filesystem is in use")); } uint32 lsn = gWadLSN; lsn += tempHed->Offset / SECTOR_SIZE; uint8 *pEEBuffer = new uint8[ FILESYS_CD_READ_SIZE + IOP_TO_EE_BUFFER_ALLIGNMENT ]; uint8 *pData = pEEBuffer; pData = ( uint8 * )( ( ( int )pEEBuffer + ( IOP_TO_EE_BUFFER_ALLIGNMENT - 1 ) ) & ~( IOP_TO_EE_BUFFER_ALLIGNMENT - 1 ) ); uint32 numSectorsToRead; uint32 err; uint32 numBytesToCopy; uint32 i; uint32 *pAI; uint32 *pDI; if ( gStreaming ) { if ( !sceCdStSeek( lsn ) ) { StartStreaming( lsn ); } } else { StartStreaming( lsn ); } while ( bytesRequired ) { if ( /*offsetWithinBuffer +*/ bytesRequired < FILESYS_CD_READ_SIZE ) { numBytesToCopy = bytesRequired; numSectorsToRead = ( ( /*offsetWithinBuffer +*/ bytesRequired ) / SECTOR_SIZE ); if ( bytesRequired & ( SECTOR_SIZE - 1 ) ) numSectorsToRead++; } else { numBytesToCopy = FILESYS_CD_READ_SIZE;// - offsetWithinBuffer; numSectorsToRead = FILESYS_NUM_SECTORS_PER_READ; } // read in a chunk at a time ( 50 sectors? 100 sectors? ) uint32 sectorsRead; sectorsRead = sceCdStRead( numSectorsToRead, ( uint32 * )pData, STMBLK, &err ); int numErrors = 0; while ( sectorsRead != numSectorsToRead ) { printf("QuickLoad: Could only read %d of the %d sectors for file %s\n", sectorsRead, numSectorsToRead, filename); sceCdStSeek( lsn ); sectorsRead = sceCdStRead( numSectorsToRead, ( uint32 * )pData, STMBLK, &err ); if ( numErrors++ >= 12 ) { // figure out what the problem is using sceCDGetError( ) and either try to // recover or put up an error screen or something? HandleCDStreamingError( lsn ); numErrors = 0; } } pAI = ( uint32 * )( addr ); pDI = ( uint32 * )( pData ); // copy the data from the buffer to the given address: for ( i = 0; i < ( numBytesToCopy >> 2 ); i++ ) { *pAI++ = *pDI++; } for ( i = 0; i < ( numBytesToCopy & 3 ); i++ ) { //addr[ 1 + ( numBytesToCopy & ~3 ) + i ] = pData[ 1 + ( numBytesToCopy & ~3 ) + i ]; addr[ ( numBytesToCopy & ~3 ) + i ] = pData[ ( numBytesToCopy & ~3 ) + i ]; } addr += numBytesToCopy; bytesRequired -= numBytesToCopy; lsn += numSectorsToRead; // offsetWithinBuffer = 0; } // sceCdStPause( ); // Mick: 8/28/02 - I added the next line // the aim is to stop streaming now, so we don't have to do the extra call to // sceCdStSeek for the next call to this functions // The speeds up the loading of skaters by 20% StopStreaming(); delete pEEBuffer; #endif // ASYNC_QUICK_FILESYS return ( true ); } static void* cdFopen(const char *fname, const char *access) { SHedFile *pHd=FindFileInHed(fname, gpHed); if (!pHd) { return NULL; } skyFile *fp = lock_skyfile(); if (!fp) { return (NULL); } int mode = skyTransMode(access); if (!mode) { unlock_skyfile( fp ); return (NULL); } fp->pFilename=fname; fp->WadOffset=pHd->Offset; fp->SOF=pHd->FileSize; fp->POS=0; fp->gdfs=0; #if ASYNC_HOST_FILESYS fp->p_async_file = NULL; #endif /* Initialise the buffer to show nothing buffered */ fp->bufferPos = READBUFFERSIZE; /* SCE_CREAT & !SCE_TRUNC mean seek to end of file */ //if (!((mode & SCE_CREAT) && !(mode & SCE_TRUNC))) //{ // fp->POS = fp->SOF; //} s_open_files++; return ((void *)fp); } static int cdFclose( void* fptr ) { skyFile* fp = (skyFile*)fptr; Dbg_AssertPtr( fptr ); if ( fp && s_open_files ) { unlock_skyfile( fp ); s_open_files--; return 0; } return -1; } static bool cdFexist( const char* name ) { if (FindFileInHed(name, gpHed)) { return true; } else { return false; } } static size_t cdFread(void *addr, size_t size, size_t count, void *fptr) { skyFile *fp = (skyFile *)fptr; size_t numBytesToRead = size * count; int bytesRead, bytesRead2; bytesRead = 0; StopStreaming( ); // char * after = (char*)addr + numBytesToRead; /* Trim number of bytes for the size of the file */ if ((fp->POS + (int32)numBytesToRead) > fp->SOF) { numBytesToRead = fp->SOF - fp->POS; } /* First try and use the buffer */ if ((fp->bufferPos < READBUFFERSIZE) && (bytesRead < (int32)numBytesToRead)) { /* Pull from the buffer */ if (numBytesToRead < (READBUFFERSIZE-fp->bufferPos)) { /* Can satisfy entirely from buffer */ bytesRead = numBytesToRead; } else { /* Pull as much as possible from the buffer */ bytesRead = READBUFFERSIZE-fp->bufferPos; } /* Copy it */ memcpy(addr, &fp->readBuffer[fp->bufferPos], bytesRead); /* Update target address and source address */ addr = (void *)((uint8 *)addr + bytesRead); fp->bufferPos += bytesRead; fp->POS += bytesRead; } /* If next bit is bigger than a buffer, read it directly and ignore the * buffer. */ if ((numBytesToRead-bytesRead) > 0) { if ((numBytesToRead-bytesRead) >= READBUFFERSIZE) { bytesRead2 = (numBytesToRead-bytesRead); //bytesRead2 = sceRead(fp->gdfs, addr, bytesRead2); Dbg_MsgAssert(WadId>=0,("Bad WadId")); //Dbg_Message("Seeking to LSN %d", fp->WadOffset+fp->POS); sceLseek(WadId, fp->WadOffset+fp->POS, SCE_SEEK_SET); bytesRead2=sceRead(WadId,addr,bytesRead2); if (bytesRead2 < 0) { bytesRead2 = 0; } } else { /* Go via the buffer */ //sceRead(fp->gdfs, fp->readBuffer, READBUFFERSIZE); Dbg_MsgAssert(WadId>=0,("Bad WadId")); //Dbg_Message("Seeking to LSN %d", fp->WadOffset+fp->POS); sceLseek(WadId, fp->WadOffset+fp->POS, SCE_SEEK_SET); sceRead(WadId,fp->readBuffer, READBUFFERSIZE); bytesRead2 = (numBytesToRead-bytesRead); memcpy(addr, fp->readBuffer, bytesRead2); fp->bufferPos = bytesRead2; } fp->POS += bytesRead2; bytesRead += bytesRead2; } return (bytesRead/size); } static size_t cdFwrite( const void *addr, size_t size, size_t count, void *fptr ) { skyFile *fp = (skyFile *)fptr; Dbg_AssertPtr( fptr ); fp->POS+=size*count; fp->SOF=fp->POS; return count; } static int cdFseek(void *fptr, long offset, int origin) { skyFile *fp = (skyFile *)fptr; int32 oldFPos, bufStart; bool noBuffer = FALSE; Dbg_AssertPtr( fptr ); oldFPos = fp->POS; bufStart = oldFPos - fp->bufferPos; if (fp->bufferPos == READBUFFERSIZE) noBuffer = TRUE; fp->bufferPos = READBUFFERSIZE; switch (origin) { case SEEK_CUR: { /* Does the seek stay in the buffer */ if ((oldFPos + offset >= bufStart) && (oldFPos + offset < bufStart+READBUFFERSIZE)) { fp->bufferPos = (oldFPos + offset) - bufStart; fp->POS += offset; } else { fp->POS+=offset; if (fp->POS<0 || fp->POS>fp->SOF) { fp->POS=-1; Dbg_MsgAssert(0,("Bad offset sent to cdFseek (SEEK_CUR) offset=%ld pos=%d size=%d",offset,fp->POS-offset,fp->SOF)); } } break; } case SEEK_END: { fp->POS=fp->SOF-offset; if (fp->POS<0 || fp->POS>fp->SOF) { fp->POS=-1; Dbg_MsgAssert(0,("Bad offset sent to cdFseek (SEEK_END)")); } break; } case SEEK_SET: { fp->POS=offset; if (fp->POS<0 || fp->POS>fp->SOF) { fp->POS=-1; Dbg_MsgAssert(0,("Bad offset sent to cdFseek (SEEK_SET)")); } break; } default: { return (-1); } } if (noBuffer) fp->bufferPos = READBUFFERSIZE; if (fp->POS == -1) { /* This may not be valid */ fp->POS = oldFPos; fp->bufferPos = READBUFFERSIZE; return (-1); } return (0); } static char * cdFgets(char *buffer, int maxLen, void *fptr) { Dbg_MsgAssert(0,("fgets not done yet")); return NULL; } static int cdFputs( const char *buffer, void *fptr) { Dbg_MsgAssert(0,("Cannot fputs to CD")); return 0; } static int cdFeof( void* fptr ) { skyFile *fp = (skyFile*)fptr; Dbg_AssertPtr( fptr ); return ( fp->POS >= fp->SOF) ; } static int cdFflush( void* ) { return 0; } /******************************************************************/ /* skyFopen */ /* */ /* On entry : Filename, access mode */ /* */ /* */ /******************************************************************/ static void* skyFopen(const char *fname, const char *access) { skyFile* fp; int mode; StopStreaming( ); /* Allocate structure for holding info */ fp = lock_skyfile(); if (!fp) { return (NULL); } mode = skyTransMode(access); if (!mode) { return (NULL); } #if ASYNC_HOST_FILESYS fp->p_async_file = CAsyncFileLoader::sOpen(fname, true); if (!fp->p_async_file) { unlock_skyfile( fp ); return (NULL); } // Get file size (will come up with better way) fp->SOF = fp->p_async_file->GetFileSize(); fp->POS = 0; //fp->p_async_file->Seek(0, SEEK_END); //fp->SOF = fp->p_async_file->WaitForIO(); //fp->p_async_file->Seek(0, SEEK_SET); //fp->POS = fp->p_async_file->WaitForIO(); #else char name[256]; char* nameptr; /* First manipulate the filename into a Sky friendly name */ if (strchr(fname, ':')) { strncpy(name, fname, 255); } else { #ifdef SCE_11 strcpy(name, "sim:"); strncpy(&name[4], fname, 251); #else strcpy(name, "host:"); strncpy(&name[5], fname, 250); #endif } /* force null termination */ name[255] = 0; nameptr = name; while(*nameptr) { if (*nameptr == '\\') *nameptr = '/'; nameptr++; } fp->gdfs = sceOpen(name, mode); if (fp->gdfs < 0) { unlock_skyfile( fp ); return (NULL); } /* We seek to the end of the file to get size */ fp->SOF = fp->POS = sceLseek(fp->gdfs, 0, SCE_SEEK_END); if (fp->SOF< 0) { sceClose(fp->gdfs); unlock_skyfile( fp ); return (NULL); } /* SCE_CREAT & !SCE_TRUNC mean seek to end of file */ if (!((mode & SCE_CREAT) && !(mode & SCE_TRUNC))) { fp->POS = sceLseek(fp->gdfs, 0, SCE_SEEK_SET); if (fp->POS < 0) { sceClose(fp->gdfs); unlock_skyfile( fp ); return (NULL); } } #endif /* Initialise the buffer to show nothing buffered */ fp->bufferPos = READBUFFERSIZE; strncpy( fp->filename, fname, MAX_PFILESYS_NAME_SIZE ); s_open_files++; return ((void *)fp); } /******************************************************************/ /* skyFclose */ /* */ /******************************************************************/ static int skyFclose( void* fptr ) { skyFile* fp = (skyFile*)fptr; Dbg_AssertPtr( fptr ); if ( fp && s_open_files ) { #if ASYNC_HOST_FILESYS CAsyncFileLoader::sClose(fp->p_async_file); fp->p_async_file = NULL; #else sceClose( fp->gdfs ); #endif unlock_skyfile( fp ); s_open_files--; return 0; } return -1; } /******************************************************************/ /* skyFexist */ /* */ /******************************************************************/ static bool skyFexist( const char* name ) { void* res = File::Open( name, "r" ); if ( res ) { File::Close( res ); return TRUE; } return FALSE; } /******************************************************************/ /* skyFread */ /* */ /* On entry : Address to read to, block size, block count, file */ /* On exit : Number of bytes read */ /* */ /******************************************************************/ static size_t skyFread(void *addr, size_t size, size_t count, void *fptr) { skyFile *fp = (skyFile *)fptr; size_t numBytesToRead = size * count; int bytesRead = 0; StopStreaming( ); /* Trim number of bytes for the size of the file */ if ((fp->POS + (int32)numBytesToRead) > fp->SOF) { numBytesToRead = fp->SOF - fp->POS; } #if ASYNC_HOST_FILESYS fp->p_async_file->Read(addr, size, count); bytesRead = fp->p_async_file->WaitForIO(); fp->POS += bytesRead; #else int bytesRead2; /* First try and use the buffer */ if ((fp->bufferPos < READBUFFERSIZE) && (bytesRead < (int32)numBytesToRead)) { /* Pull from the buffer */ if (numBytesToRead < (READBUFFERSIZE-fp->bufferPos)) { /* Can satisfy entirely from buffer */ bytesRead = numBytesToRead; } else { /* Pull as much as possible from the buffer */ bytesRead = READBUFFERSIZE-fp->bufferPos; } /* Copy it */ memcpy(addr, &fp->readBuffer[fp->bufferPos], bytesRead); /* Update target address and source address */ addr = (void *)((uint8 *)addr + bytesRead); fp->bufferPos += bytesRead; fp->POS += bytesRead; } /* If next bit is bigger than a buffer, read it directly and ignore the * buffer. */ if ((numBytesToRead-bytesRead) > 0) { if ((numBytesToRead-bytesRead) >= READBUFFERSIZE) { bytesRead2 = (numBytesToRead-bytesRead); bytesRead2 = sceRead(fp->gdfs, addr, bytesRead2); if (bytesRead2 < 0) { bytesRead2 = 0; } } else { /* Go via the buffer */ sceRead(fp->gdfs, fp->readBuffer, READBUFFERSIZE); bytesRead2 = (numBytesToRead-bytesRead); memcpy(addr, fp->readBuffer, bytesRead2); fp->bufferPos = bytesRead2; } fp->POS += bytesRead2; bytesRead += bytesRead2; } #endif return (bytesRead/size); } /******************************************************************/ /* skyFwrite */ /* */ /* On entry : Address to write from, block size, block count, file*/ /* On exit : Number of bytes written */ /* */ /******************************************************************/ static size_t skyFwrite( const void *addr, size_t size, size_t count, void *fptr ) { int bytesWritten; skyFile* fp = (skyFile*)fptr; int32 numBytesToWrite = size * count; Dbg_AssertPtr( addr ); Dbg_AssertPtr( fptr ); bytesWritten = sceWrite( fp->gdfs, (void*)addr, numBytesToWrite ); if (bytesWritten != -1) { fp->POS += bytesWritten; if (fp->POS > fp->SOF) fp->SOF = fp->POS; return (size>0?bytesWritten/size:0); } return (0); } /******************************************************************/ /* skyFseek */ /* */ /* On entry : file to seek in, offset, how to seek */ /* On exit : success/failure */ /* */ /******************************************************************/ static int skyFseek(void *fptr, long offset, int origin) { skyFile *fp = (skyFile *)fptr; Dbg_AssertPtr( fptr ); StopStreaming( ); #if ASYNC_HOST_FILESYS fp->p_async_file->Seek(offset, origin); fp->POS = fp->p_async_file->WaitForIO(); #else int32 oldFPos, bufStart; bool noBuffer = FALSE; oldFPos = fp->POS; bufStart = oldFPos - fp->bufferPos; if (fp->bufferPos == READBUFFERSIZE) noBuffer = TRUE; fp->bufferPos = READBUFFERSIZE; switch (origin) { case SEEK_CUR: { /* Does the seek stay in the buffer */ if ((oldFPos + offset >= bufStart) && (oldFPos + offset < bufStart+READBUFFERSIZE)) { fp->bufferPos = (oldFPos + offset) - bufStart; fp->POS += offset; } else { fp->POS = sceLseek(fp->gdfs, oldFPos + offset, SCE_SEEK_SET); } break; } case SEEK_END: { fp->POS = sceLseek(fp->gdfs, offset, SCE_SEEK_END); break; } case SEEK_SET: { fp->POS = sceLseek(fp->gdfs, offset, SCE_SEEK_SET); break; } default: { return (-1); } } if (noBuffer) fp->bufferPos = READBUFFERSIZE; if (fp->POS == -1) { /* This may not be valid */ fp->POS = oldFPos; fp->bufferPos = READBUFFERSIZE; return (-1); } #endif return (0); } /******************************************************************/ /* skyFgets */ /* */ /* On entry : Buffer to read into, max chars to read, file */ /* On exit : Non negative value on success */ /* */ /******************************************************************/ static char * skyFgets(char *buffer, int maxLen, void *fptr) { skyFile *fp = (skyFile *) fptr; int32 i; int32 numBytesRead; Dbg_AssertPtr( buffer ); Dbg_AssertPtr( fptr ); i = 0; numBytesRead = skyFread(buffer, 1, maxLen - 1, fp); if (numBytesRead == 0) { return (NULL); } while (i < numBytesRead) { if (buffer[i] == '\n') { i++; buffer[i] = '\0'; /* the file pointer needs */ /* to be reset as skyFread */ /* will have overshot the */ /* first new line */ i -= numBytesRead; skyFseek(fp, i, SEEK_CUR); return (buffer); } else if ( buffer[i] == 0x0D ) { if ((i < (numBytesRead - 1)) && (buffer[i + 1] == '\n')) { memcpy(&buffer[i], &buffer[i + 1], (numBytesRead - i - 1)); numBytesRead--; } else i++; } else i++; } /* * Don't return NULL because we could have read maxLen bytes * without finding a \n */ return (buffer); } /******************************************************************/ /* skyFputs */ /* */ /* On entry : Buffer to write from, file to write to */ /* On exit : Non negative value on success */ /* */ /******************************************************************/ static int skyFputs( const char *buffer, void *fptr) { skyFile *fp = (skyFile *)fptr; int i, j; Dbg_AssertPtr( buffer ); Dbg_AssertPtr( fptr ); i = strlen(buffer); j = sceWrite(fp->gdfs, (void*)buffer, i); if (j != -1) { fp->POS += j; if (fp->POS > fp->SOF) fp->SOF = fp->POS; } if ((j == -1) || (i != j)) { return (EOF); } return (j); } /******************************************************************/ /* skyFeof */ /* */ /* On entry : File to test for eof */ /* On exit : Non zero if end of file reached */ /* */ /******************************************************************/ static int skyFeof( void* fptr ) { skyFile *fp = (skyFile*)fptr; Dbg_AssertPtr( fptr ); return ( fp->POS >= fp->SOF) ; } /******************************************************************/ /* */ /* */ /******************************************************************/ static int skyFflush( void* ) { return 0; } /***************************************************************************** ** Public Functions ** *****************************************************************************/ long GetFileSize(void *pFP) { Dbg_MsgAssert(pFP,("NULL pFP sent to GetFileSize")); #if ASYNC_HOST_FILESYS skyFile *fp = (skyFile *)pFP; if (!Config::CD() && fp->p_async_file) { return fp->p_async_file->GetFileSize(); } #endif if (PreMgr::sPreEnabled()) { int retval = PreMgr::pre_get_file_size((PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } return ((skyFile*)pFP)->SOF; } long GetFilePosition(void *pFP) { Dbg_MsgAssert(pFP,("NULL pFP sent to GetFilePosition")); if (PreMgr::sPreEnabled()) { int retval = PreMgr::pre_get_file_position((PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } return ((skyFile*)pFP)->POS; } float GetPercentageRead(void *pFP) { Dbg_MsgAssert(pFP,("NULL pFP sent to GetPercentageRead")); if (PreMgr::sPreEnabled()) { int position = PreMgr::pre_get_file_position((PreFile::FileHandle *) pFP); int size = PreMgr::pre_get_file_size((PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return 100.0 * position / size; } return 100.0*((skyFile*)pFP)->POS/((skyFile*)pFP)->SOF; } void InstallFileSystem( void ) { // cd builds can only have one file open at any given time int max_open_files=Config::CD() ? 1:MAXOPENFILES; // initialize the locks on the sky files for ( int i = 0; i < max_open_files; i++ ) { g_skyFile[i].locked = false; } if (Config::CD()) { int disk_type = SCECdDVD; // Initialise the CD printf("Initialising CD ...\n"); // int err1 = sceCdInit(SCECdINIT); //int err2 = sceCdMmode(SCECdDVD); // int err2 = sceCdMmode(disk_type); // find the disk type, this might be different from what we intialized to printf(" sceCdGetDiskType "); int detected_disk_type= sceCdGetDiskType(); switch(detected_disk_type) { case SCECdIllgalMedia: printf("Disk Type= IllgalMedia\n"); break; case SCECdPS2DVD: printf("Disk Type= PlayStation2 DVD\n"); break; case SCECdPS2CD: printf("Disk Type= PlayStation2 CD\n"); break; case SCECdPS2CDDA: printf("Disk Type= PlayStation2 CD with CDDA\n"); break; case SCECdPSCD: printf("Disk Type= PlayStation CD\n"); break; case SCECdPSCDDA: printf("Disk Type= PlayStation CD with CDDA\n"); break; case SCECdDVDV: printf("Disk Type= DVD video\n"); break; case SCECdCDDA: printf("Disk Type= CD-DA\n"); break; case SCECdDETCT: printf("Working\n"); break; case SCECdNODISC: printf("Disk Type= No Disc\n"); break; default: printf("Disk Type= OTHER DISK\n"); break; } // If disk type has changed to a CD, then need to re-initialize it if (detected_disk_type == SCECdPS2CD) { #if __DVD_ONLY__ printf( "*** ERROR - CD Detected, needs DVD.\n" ); #else printf( "reinitializing for Ps2CD\n" ); disk_type = SCECdCD; sceCdMmode(disk_type); printf( "done reinitializing\n" ); #endif } // printf("'tis done. errs = %d, %d\n",err1,err2); // This next bit is essential for when making a bootable disc, ie one that will // boot on the actual PS rather than just the dev system. //#ifdef __NOPT_BOOTABLE__ // K: Commented out __NOPT_BOOTABLE__, since it is set when __NOPT_CDROM__OLD is set. /* Reboot IOP, replace default modules */ char path[128]; // sprintf(path,"cdrom0:\\%sIOP\\IOPRP260.IMG;1",Config::GetDirectory()); // sprintf(path,"host0:\\SKATE5\\DATA\\IOPMODULES\\DNAS280.IMG;1"); sprintf(path,"cdrom0:\\%sIOP\\DNAS280.IMG;1",Config::GetDirectory()); while ( !sceSifRebootIop((const char*) path) ); /* (Important) Unlimited retries */ while( !sceSifSyncIop() ); /* Reinitialize */ sceSifInitRpc(0); sceCdInit(SCECdINIT); // sceCdMmode(SCECdDVD); /* Media: CD-ROM */ sceCdMmode(disk_type); /* Media: CD-ROM */ sceFsReset(); //#endif // __NOPT_BOOTABLE__ printf("Opening hed file ...\n"); // Open the hed file and load it into memory. gpHed = LoadHed( "SKATE5"); /* printf ("Size = %d, adjusted = %d\n",HedSize,(HedSize+2047)&~204); for (int i = HedSize; i< (HedSize+2047)&~204; i++) { if (p[i] != 0x55) { printf ("Overrun ? %d = %2x\n",i,p[i]); break; } } */ printf("Opening wad file ...\n"); // Open the wad and keep it open for future reference. Dbg_MsgAssert(WadId==-1,("WadId not -1 ?")); while (WadId<0) { char path[128]; sprintf(path,"cdrom0:\\%sSKATE5.WAD;1",Config::GetDirectory()); WadId=sceOpen(path,SCE_RDONLY); if (WadId<0) { printf("Retrying opening SKATE5.wad ...\n"); } } printf( "opened successfully\n" ); } } /***************************************************************************** ** Public Functions ** *****************************************************************************/ void UninstallFileSystem( void ) { if (Config::CD()) { // Free the hed file buffer. Dbg_MsgAssert(gpHed,("NULL gpHed ?")); Mem::Free(gpHed); gpHed=NULL; Dbg_MsgAssert(WadId>=0,("Bad WadId")); sceClose(WadId); WadId=-1; // free IOP memory: if ( gFilesysIOPStreamBuffer ) { sceSifFreeIopHeap( gFilesysIOPStreamBuffer ) ; gFilesysIOPStreamBuffer = 0; } } } void InitQuickFileSystem( void ) { // get the LSN to allow us to figure out where every other file is... sceCdlFILE wadInfo; char path[128]; sprintf(path,"\\%sSKATE5.WAD;1",Config::GetDirectory()); while ( !sceCdSearchFile( &wadInfo, path ) ) { printf("Retrying finding SKATE5.wad ...\n"); } //printf( "after search file\n" ); gWadLSN = wadInfo.lsn; #if DISABLE_QUICK_FILESYSTEM return; #endif #if ASYNC_QUICK_FILESYS == 0 // allocate our streaming buffer (iop side) for files: //printf( "before alloc heap\n" ); gFilesysIOPStreamBuffer = sceSifAllocIopHeap( FILESYS_IOP_BUFFER_SIZE ); //printf( "after alloc heap: buffer %x of size %d\n", gFilesysIOPStreamBuffer, FILESYS_IOP_BUFFER_SIZE ); Dbg_MsgAssert( gFilesysIOPStreamBuffer,( "IOP stream buffer allocation failed." )); //printf( "before stream init\n" ); // initialize streaming capabilities and shit... while ( !sceCdStInit( FILESYS_NUM_SECTORS_IN_BUFFER, FILESYS_STREAM_BUFFER_NUM_PARTITIONS, (unsigned int)gFilesysIOPStreamBuffer ) ) { printf( "trying to init CD stream\n" ); } //printf( "after stream init\n" ); StartStreaming( gWadLSN ); #endif gQuickFileSystemInitialzed = true; //printf( "after StartStreaming()\n" ); } // end of InitQuickFileSystem( ) // We need to execute this after we have used the Cd Stream calls in other routines void ResetQuickFileSystem( void ) { #if ASYNC_QUICK_FILESYS == 0 Dbg_MsgAssert( gFilesysIOPStreamBuffer,( "IOP stream buffer not initialized" )); // initialize streaming capabilities and shit... while ( !sceCdStInit( FILESYS_NUM_SECTORS_IN_BUFFER, FILESYS_STREAM_BUFFER_NUM_PARTITIONS, (unsigned int)gFilesysIOPStreamBuffer ) ) { printf( "trying to init CD stream\n" ); } #endif } //////////////////////////////////////////////////////////////////// // Our versions of the ANSI file IO functions. They call // the PreMgr first to see if the file is in a PRE file. //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // Exist bool Exist( const char *filename ) { if (PreMgr::sPreEnabled()) { bool retval = PreMgr::pre_fexist(filename); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFexist(filename); } else { return skyFexist(filename); } } //////////////////////////////////////////////////////////////////// // Open void * Open( const char *filename, const char *access ) { bool use_pre = true; #if ASYNC_HOST_FILESYS use_pre = Config::CD(); #endif // Don't use pre files if writing to disk (eg when writing parks) if (access[0]=='w') { use_pre=false; } if (Script::GetInt(CRCD(0xe99935c2,"show_filenames"),false)) { printf (".... Open %s\n",filename); } if (use_pre && PreMgr::sPreEnabled()) { void * retval = PreMgr::pre_fopen(filename, access); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFopen(filename, access); } else { return skyFopen(filename, access); } } //////////////////////////////////////////////////////////////////// // Close int Close( void *pFP ) { bool use_pre = true; #if ASYNC_HOST_FILESYS use_pre = Config::CD(); #endif if (use_pre && PreMgr::sPreEnabled()) { int retval = PreMgr::pre_fclose((PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFclose(pFP); } else { return skyFclose(pFP); } } //////////////////////////////////////////////////////////////////// // Read size_t Read( void *addr, size_t size, size_t count, void *pFP ) { bool use_pre = true; #if ASYNC_HOST_FILESYS use_pre = Config::CD(); #endif if (use_pre && PreMgr::sPreEnabled()) { size_t retval = PreMgr::pre_fread(addr, size, count, (PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } if (CAsyncFileLoader::sAsyncInUse()) { CAsyncFileLoader::sWaitForIOEvent(true); Dbg_Message("************************ Can't do a normal read when async filesystem is in use"); //Dbg_MsgAssert(0, ("Can't do a normal read when async filesystem is in use")); } if (Config::CD()) { return cdFread(addr, size, count, pFP); } else { return skyFread(addr, size, count, pFP); } } /////////////////////////////////////////////////////////////////////// // Read an Integer in PS2 (littleendian) format // we just read it directly into memory... size_t ReadInt( void *addr, void *pFP ) { return Read(addr,4,1,pFP); } //////////////////////////////////////////////////////////////////// // Write size_t Write( const void *addr, size_t size, size_t count, void *pFP ) { if (PreMgr::sPreEnabled()) { size_t retval = PreMgr::pre_fwrite(addr, size, count, (PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFwrite(addr, size, count, pFP); } else { return skyFwrite(addr, size, count, pFP); } } //////////////////////////////////////////////////////////////////// // GetS char * GetS( char *buffer, int maxlen, void *pFP ) { if (PreMgr::sPreEnabled()) { char * retval = PreMgr::pre_fgets(buffer, maxlen, (PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFgets(buffer, maxlen, pFP); } else { return skyFgets(buffer, maxlen, pFP); } } //////////////////////////////////////////////////////////////////// // PutS int PutS( const char *buffer, void *pFP ) { if (PreMgr::sPreEnabled()) { int retval = PreMgr::pre_fputs(buffer, (PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFputs(buffer, pFP); } else { return skyFputs(buffer, pFP); } } //////////////////////////////////////////////////////////////////// // Eof int Eof( void *pFP ) { bool use_pre = true; #if ASYNC_HOST_FILESYS use_pre = Config::CD(); #endif if (use_pre && PreMgr::sPreEnabled()) { int retval = PreMgr::pre_feof((PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFeof(pFP); } else { return skyFeof(pFP); } } //////////////////////////////////////////////////////////////////// // Seek int Seek( void *pFP, long offset, int origin ) { bool use_pre = true; #if ASYNC_HOST_FILESYS use_pre = Config::CD(); #endif if (use_pre && PreMgr::sPreEnabled()) { int retval = PreMgr::pre_fseek((PreFile::FileHandle *) pFP, offset, origin); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFseek(pFP, offset, origin); } else { return skyFseek(pFP, offset, origin); } } int32 Tell( void* pFP ) { skyFile* fp = (skyFile *) pFP; return fp->POS; } //////////////////////////////////////////////////////////////////// // Flush int Flush( void *pFP ) { bool use_pre = true; #if ASYNC_HOST_FILESYS use_pre = Config::CD(); #endif if (use_pre && PreMgr::sPreEnabled()) { int retval = PreMgr::pre_fflush((PreFile::FileHandle *) pFP); if (PreMgr::sPreExecuteSuccess()) return retval; } if (Config::CD()) { return cdFflush(pFP); } else { return skyFflush(pFP); } } /******************************************************************/ /* */ /* */ /******************************************************************/ } // namespace File