thug/Code/Sys/File/PRE.cpp
2016-02-14 08:39:12 +11:00

1621 lines
40 KiB
C++

/*****************************************************************************
** **
** Neversoft Entertainment. **
** **
** Copyright (C) 2000 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: skate3 **
** **
** Module: **
** **
** File name: **
** **
** Created by: rjm **
** **
** Description: **
** **
*****************************************************************************/
// start autoduck documentation
// @DOC pre
// @module pre | None
// @subindex Scripting Database
// @index script | pre
/*****************************************************************************
** Includes **
*****************************************************************************/
#ifdef __PLAT_NGPS__
#include <eekernel.h>
#endif // __PLAT_NGPS__
#include <core/defines.h>
//#include <core/HashTable.h>
#include <core/StringHashTable.h>
#include <sys/File/PRE.h>
#include <sys/file/filesys.h>
#include <sys/file/AsyncFilesys.h>
#include <sys/config/config.h>
// cd shared by the music streaming stuff... ASSERT if file access attempted
// while music is streaming:
#include <gel/music/music.h>
// script stuff
#include <gel/scripting/struct.h>
#include <gel/scripting/symboltable.h>
#ifdef __PLAT_NGC__
#include "sys/ngc/p_aram.h"
#include "sys/ngc/p_dma.h"
#include "sys/ngc/p_display.h"
#ifdef __NOPT_FINAL__
#define __PRE_ARAM__
#endif // __NOPT_FINAL__
#endif
/*****************************************************************************
** DBG Information **
*****************************************************************************/
#define DEBUG_PRE 0
#define PRE_NAME_OFFSET 16 // was 12, but then I added an extra checksum field
namespace File
{
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
#define CURRENT_PRE_VERSION 0xabcd0003 // as of 3/14/2001
//#define CURRENT_PRE_VERSION 0xabcd0001 // until 3/14/2001
#define RINGBUFFERSIZE 4096 /* N size of ring buffer */
#define MATCHLIMIT 18 /* F upper limit for match_length */
#define THRESHOLD 2 /* encode string into position and length */
#define WriteOut(x) {*pOut++ = x;}
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
PreMgr *PreMgr::sp_mgr = NULL;
bool PreMgr::s_lastExecuteSuccess = false;
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
#define USE_BUFFER 1 // we don't need no stinking buffer!!!!
#if USE_BUFFER
#ifdef __PLAT_NGPS__
// ring buffer is just over 4K 4096+17),
// so fits nicely in the PS2's 16K scratchpad
unsigned char text_buf[RINGBUFFERSIZE + MATCHLIMIT - 1];
// Note: if we try to use the scratchpad, like this
// then the code actually runs slower
// if we want to optimize this, then it should
// be hand crafted in assembly, using 128bit registers
// const unsigned char * text_buf = (unsigned char*) 0x70000000;
// #define text_buf ((unsigned char*) 0x70000000)
#else
unsigned char
text_buf[RINGBUFFERSIZE + MATCHLIMIT - 1]; /* ring buffer of size N,
with extra F-1 bytes to facilitate string comparison */
#endif
#endif
#ifdef __PRE_ARAM__
#define ReadInto(x) if (!Len) break; Len--; x = get_byte( p_buffer )
#define ReadInto2(x) Len--; x = get_byte( p_buffer ) // version that knows Len is Ok
#else
#define ReadInto(x) if (!Len) break; Len--; x = *pIn++
#define ReadInto2(x) Len--; x = *pIn++ // version that knows Len is Ok
#endif // __PRE_ARAM__
// Decode an LZSS encoded stream
// Runs at approx 12MB/s on PS2 without scratchpad (which slows it down in C)
// a 32x CD would run at 4.8MB/sec, although we seem to get a lot less than this
// with our current file system, more like 600K per seconds.....
// Need to write a fast streaming file system....
#ifdef __PRE_ARAM__
#define INPUT_BUFFER_SIZE (16 * 1024)
static uint32 p_aram_in;
static int aram_len = 0;
static int buffer_bytes = 0;
static int buffer_offset = 0;
static unsigned char get_byte( unsigned char * p_buffer )
{
if ( buffer_bytes == 0 )
{
int toread;
if ( aram_len >= INPUT_BUFFER_SIZE )
{
toread = INPUT_BUFFER_SIZE;
}
else
{
toread = aram_len;
}
NsDMA::toMRAM( p_buffer, p_aram_in, toread );
p_aram_in += INPUT_BUFFER_SIZE;
aram_len -= INPUT_BUFFER_SIZE;
buffer_bytes = toread;
buffer_offset = 0;
}
unsigned char bb = p_buffer[buffer_offset];
buffer_bytes--;
buffer_offset++;
return bb;
}
#endif // __PRE_ARAM__
void DecodeLZSS(unsigned char *pIn, unsigned char *pOut, int Len) /* Just the reverse of Encode(). */
{
int i, j, k, r, c;
// uint64 LongWord;
// int bytes = 0;
// unsigned char *pScratch;
unsigned int flags;
// Ensure we have decent values for the decode length
Dbg_Assert(( Len >= 0 ) && ( Len < 0x2000000 ));
if(( Len < 0 ) || ( Len >= 0x2000000 ))
{
while( 1 );
}
#ifdef __PRE_ARAM__
unsigned char p_buffer[INPUT_BUFFER_SIZE];
p_aram_in = (uint32)pIn;
aram_len = Len;
#endif // __PRE_ARAM__
// int basetime = (int) Tmr::ElapsedTime(0);
// int len = Len;
// int OutBytes = 4;
// int OutWord = 0;
#if USE_BUFFER
for (i = 0; i < RINGBUFFERSIZE - MATCHLIMIT; i++)
text_buf[i] = ' ';
r = RINGBUFFERSIZE - MATCHLIMIT;
#else
r = RINGBUFFERSIZE - MATCHLIMIT;
#endif
flags = 0;
for ( ; ; )
{
if (((flags >>= 1) & 256) == 0)
{
ReadInto(c);
flags = c | 0xff00; /* uses higher byte cleverly */
} /* to count eight */
if (flags & 1)
{
ReadInto(c);
// putc(c, outfile);
WriteOut(c);
#if USE_BUFFER
text_buf[r++] = c;
r &= (RINGBUFFERSIZE - 1);
#else
r++;
// r &= (RINGBUFFERSIZE - 1); // don't need to wrap r until it is used
#endif
}
else
{
ReadInto(i);
ReadInto2(j); // note, don't need to check len on this one....
i |= ((j & 0xf0) << 4); // i is 12 bit offset
#if !USE_BUFFER
j = (j & 0x0f) + THRESHOLD+1; // j is 4 bit length (above the threshold)
unsigned char *pStream;
r &= (RINGBUFFERSIZE - 1); // wrap r around before it is used
pStream = pOut - r; // get base of block
if (i>=r) // if offset > r, then
pStream -= RINGBUFFERSIZE; // it's the previous block
pStream += i; // add in the offset to the base
r+=j; // add size to r
while (j--) // copy j bytes
WriteOut(*pStream++);
#else
j = (j & 0x0f) + THRESHOLD; // j is 4 bit length (above the threshold)
for (k = 0; k <= j; k++) // just copy the bytes
{
c = text_buf[(i+k) & (RINGBUFFERSIZE - 1)];
WriteOut(c);
text_buf[r++] = c;
r &= (RINGBUFFERSIZE - 1);
}
#endif
}
}
// int Time = (int) Tmr::ElapsedTime(basetime);
// if (Time > 5)
// {
// printf("decomp time is %d ms, for %d bytes, %d bytes/second\n", Time,len, len * 1000 /Time );
// }
}
void EndOfDecodeLZSS( void )
{
}
void PreFile::s_delete_file(_File *pFile, void *pData)
{
Dbg_Assert(pFile);
delete pFile;
}
PreFile::PreFile(uint8 *p_file_buffer, bool useBottomUpHeap)
{
m_use_bottom_up_heap=useBottomUpHeap;
mp_table = new Lst::StringHashTable<_File>(4);
Dbg_AssertPtr(p_file_buffer);
mp_buffer = p_file_buffer;
#ifdef __NOPT_ASSERT__
#ifdef __PRE_ARAM__
uint version;
NsDMA::toMRAM( &version, (uint32)mp_buffer + 4, 4 );
#else
uint version = *((int *) (mp_buffer + 4));
#endif // __PRE_ARAM__
Dbg_MsgAssert(version == CURRENT_PRE_VERSION,( "PRE file version (%x) not current (%x)",version,CURRENT_PRE_VERSION));
#endif
#ifdef __PRE_ARAM__
NsDMA::toMRAM( &m_numEntries, (uint32)mp_buffer + 8, 4 );
#else
m_numEntries = *((int *)(mp_buffer + 8));
#endif // __PRE_ARAM__
uint8 *pEntry = mp_buffer + 12;
for (int i = 0; i < m_numEntries; i++)
{
#ifdef __PRE_ARAM__
int data_size;
int compressed_data_size;
short text_size;
NsDMA::toMRAM( &data_size, (uint32)pEntry, 4 );
NsDMA::toMRAM( &compressed_data_size, (uint32)pEntry + 4, 4 );
NsDMA::toMRAM( &text_size, (uint32)pEntry + 8, 2 );
#else
int data_size = *((int *) pEntry);
int compressed_data_size = *((int *) (pEntry + 4));
int text_size = *((short *) (pEntry + 8));
#endif // __PRE_ARAM__
int actual_data_size = (compressed_data_size != 0) ? compressed_data_size : data_size;
#ifdef __PRE_ARAM__
char pName[1024];
NsDMA::toMRAM( pName, (uint32)pEntry + PRE_NAME_OFFSET, text_size );
#else
char *pName = (char *) pEntry + PRE_NAME_OFFSET;
#endif // __PRE_ARAM__
uint8 *pCompressedData = pEntry + PRE_NAME_OFFSET + text_size;
_File *pFile;
if (m_use_bottom_up_heap)
{
pFile = new (Mem::Manager::sHandle().BottomUpHeap()) _File;
}
else
{
pFile = new (Mem::Manager::sHandle().TopDownHeap()) _File;
}
if (!mp_table->GetItem(pName))
{
// file is not in table, safe to add
mp_table->PutItem(pName, pFile);
pFile->compressedDataSize = compressed_data_size;
pFile->pCompressedData = pCompressedData;
pFile->pData = NULL;
pFile->m_position = 0;
pFile->m_filesize = data_size;
}
else
// Somehow, file is already in table, just kill it
// Later, I'll want to add an assert
delete pFile;
# if DEBUG_PRE
printf(" %s, size %d\n", pName, data_size);
# endif
pEntry += PRE_NAME_OFFSET + text_size + ((actual_data_size + 3) & (~3));
}
# if DEBUG_PRE
printf("Done loading PRE\n");
# endif
mp_activeFile = NULL;
m_numOpenAsyncFiles = 0;
}
PreFile::~PreFile()
{
#ifdef __PRE_ARAM__
NsARAM::free( (uint32)mp_buffer );
#else
delete mp_buffer;
#endif // __PRE_ARAM__
mp_table->HandleCallback(s_delete_file, NULL);
mp_table->FlushAllItems();
delete mp_table;
Dbg_MsgAssert(m_numOpenAsyncFiles == 0, ("Can't unload Pre because there are still %d async files open", m_numOpenAsyncFiles));
}
bool PreFile::FileExists(const char *pName)
{
_File *pFile = mp_table->GetItem(pName, false);
return (pFile != NULL);
}
// returns handle pointer
PreFile::FileHandle *PreFile::GetContainedFile(const char *pName)
{
_File *pFile = mp_table->GetItem(pName, false);
if (!pFile)
return NULL;
PreFile::FileHandle *pHandle = pFile;
//// kinda roundabout, but sets mp_activeFile
GetContainedFileByHandle(pHandle);
#ifdef __PLAT_NGC__
NsDisplay::doReset();
#endif // __PLAT_NGC__
// do we need to fetch file data?
if (!mp_activeFile->pData)
{
if (mp_activeFile->compressedDataSize)
{
Mem::PushMemProfile((char*)pName);
if (m_use_bottom_up_heap)
{
mp_activeFile->pData = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[mp_activeFile->m_filesize];
}
else
{
mp_activeFile->pData = new (Mem::Manager::sHandle().TopDownHeap()) uint8[mp_activeFile->m_filesize];
}
Mem::PopMemProfile();
// need to uncompress data
DecodeLZSS(mp_activeFile->pCompressedData, mp_activeFile->pData, mp_activeFile->compressedDataSize);
}
#ifdef __PRE_ARAM__
else
{
// Just DMA to main RAM.
Mem::PushMemProfile((char*)pName);
if (m_use_bottom_up_heap)
{
mp_activeFile->pData = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[mp_activeFile->m_filesize];
}
else
{
mp_activeFile->pData = new (Mem::Manager::sHandle().TopDownHeap()) uint8[mp_activeFile->m_filesize];
}
Mem::PopMemProfile();
NsDMA::toMRAM( mp_activeFile->pData, (uint32)mp_activeFile->pCompressedData, mp_activeFile->m_filesize );
}
#endif // __PRE_ARAM__
}
return pHandle;
}
// allocate memory and load file directly from a pre file, if it is there
// This avoids the problem of having to have the decompressed file in memory twice
// when we are loading directly, like with Pip::Load()
// using this enables us to actually load network game, where there is 1MB less heap during loading
//
// returns a pointer to the file in memory
// or NULL if the file is not in this pre file.
// optional parameter p_dest, if set to anything other then NULL, then load file to this destination
void *PreFile::LoadContainedFile(const char *pName,int *p_size, void *p_dest)
{
// printf ("LoadContainedFile(%s\n",pName);
_File *pFile = mp_table->GetItem(pName, false);
if (!pFile)
{
return NULL;
}
*p_size = pFile->m_filesize;
// If destination was passed as NULL, then allocate memory
if (!p_dest)
{
p_dest = Mem::Malloc(pFile->m_filesize);
}
// do we need to deompress file data?
if (!pFile->pData)
{
if (pFile->compressedDataSize)
{
// need to uncompress data
//DecodeLZSS(mp_activeFile->pCompressedData, mp_activeFile->pData, mp_activeFile->compressedDataSize);
DecodeLZSS(pFile->pCompressedData, (uint8*)p_dest, pFile->compressedDataSize);
}
else
{
#ifdef __PRE_ARAM__
// Just DMA to main RAM.
NsDMA::toMRAM( p_dest, (uint32)pFile->pCompressedData, pFile->m_filesize );
#else
memcpy(p_dest,(void*)pFile->pCompressedData,pFile->m_filesize);
#endif // __PRE_ARAM__
}
}
else
{
// printf ("Copying %d bytes from %p to %p\n",pFile->sky.SOF,p_dest,(void*)pFile->pData);
memcpy(p_dest,(void*)pFile->pData,pFile->m_filesize);
}
return p_dest;
}
uint8 *PreFile::GetContainedFileByHandle(PreFile::FileHandle *pHandle)
{
mp_table->IterateStart();
_File *pFile = mp_table->IterateNext();
while(pFile)
{
uint8 *pCompressedData = pFile->pCompressedData;
if (pCompressedData && pFile == pHandle)
{
mp_activeFile = pFile;
if (mp_activeFile->compressedDataSize)
return mp_activeFile->pData;
else
return mp_activeFile->pCompressedData;
}
pFile = mp_table->IterateNext();
}
return NULL;
}
void PreFile::Reset()
{
Dbg_AssertPtr(mp_activeFile);
mp_activeFile->m_position = 0;
}
uint32 PreFile::Read(void *addr, uint32 count)
{
Dbg_AssertPtr(mp_activeFile);
int seek_offs = mp_activeFile->m_position;
unsigned int limit = mp_activeFile->m_filesize - seek_offs;
int copy_number = (count <= limit) ? count : limit;
#ifdef __PRE_ARAM__
memcpy(addr, mp_activeFile->pData + mp_activeFile->m_position, copy_number);
#else
if (mp_activeFile->compressedDataSize)
{
Dbg_MsgAssert(mp_activeFile->pData,( "file not uncompressed"));
memcpy(addr, mp_activeFile->pData + mp_activeFile->m_position, copy_number);
}
else
{
memcpy(addr, mp_activeFile->pCompressedData + mp_activeFile->m_position, copy_number);
}
#endif // __PRE_ARAM__
mp_activeFile->m_position += copy_number;
#if DEBUG_PRE
printf("PRE: read %d bytes from file, handle 0x%x\n", copy_number, (int) mp_activeFile->pData);
#endif
return copy_number;
}
int PreFile::Eof()
{
Dbg_AssertPtr(mp_activeFile);
if (mp_activeFile->m_position >= mp_activeFile->m_filesize)
{
#if DEBUG_PRE
printf("PRE: at end of file\n");
#endif
return 1;
}
#if DEBUG_PRE
printf("PRE: not at end of file\n");
#endif
return 0;
}
void PreFile::Open(bool async)
{
if (async)
{
m_numOpenAsyncFiles++;
}
}
void PreFile::Close(bool async)
{
//Dbg_MsgAssert(mp_activeFile->pData,( "file not uncompressed"));
if (mp_activeFile->pData)
delete mp_activeFile->pData;
mp_activeFile->pData = NULL;
if (async)
{
m_numOpenAsyncFiles--;
Dbg_MsgAssert(m_numOpenAsyncFiles >= 0, ("PreFile: m_numOpenAsyncFiles is negative after Close()"));
}
}
int PreFile::Seek(long offset, int origin)
{
int32 old_pos = mp_activeFile->m_position;
// SEEK_CUR, SEEK_END, SEEK_SET
switch(origin)
{
case SEEK_CUR:
mp_activeFile->m_position += offset;
break;
case SEEK_END:
mp_activeFile->m_position = mp_activeFile->m_filesize - offset;
break;
case SEEK_SET:
mp_activeFile->m_position = offset;
break;
default:
return -1;
}
if (mp_activeFile->m_position < 0 || mp_activeFile->m_position > mp_activeFile->m_filesize)
{
mp_activeFile->m_position = old_pos;
return -1;
}
return 0;
}
PreMgr::PreMgr()
{
mp_table = new Lst::StringHashTable<PreFile>(4);
sp_mgr = this;
mp_activeHandle = NULL;
mp_activeData = NULL;
mp_activeNonPreHandle = NULL;
m_num_pending_pre_files = 0;
}
PreMgr::~PreMgr()
{
delete mp_table;
}
// Returns handle
// Not frequently called
PreFile::FileHandle *PreMgr::getContainedFile(const char *pName)
{
Dbg_AssertPtr(pName);
// replace all '/' with '\'
char cleaned_name[128];
const char *pCharIn = pName;
char *pCharOut = cleaned_name;
while (1)
{
*pCharOut = *pCharIn;
if (*pCharIn == '\0') break;
if (*pCharOut == '/') *pCharOut = '\\';
pCharIn++;
pCharOut++;
}
PreFile::FileHandle *pHandle = NULL;
mp_table->IterateStart();
PreFile *pPre = mp_table->IterateNext();
while(pPre)
{
pHandle = pPre->GetContainedFile(cleaned_name);
if (pHandle)
{
mp_activePre = pPre;
mp_activeHandle = pHandle;
mp_activeData = pPre->GetContainedFileByHandle(pHandle);
# ifdef __PLAT_NGPS__
scePrintf("+++ %s is in PRE\n", cleaned_name);
# endif
return pHandle;
}
pPre = mp_table->IterateNext();
}
# ifdef __PLAT_NGPS__
scePrintf("--- %s not found in PRE\n", cleaned_name);
# endif
return NULL;
}
// returns true if the file exists in any of the pre files
bool PreMgr::fileExists(const char *pName)
{
Dbg_AssertPtr(pName);
// replace all '/' with '\'
char cleaned_name[128];
const char *pCharIn = pName;
char *pCharOut = cleaned_name;
while (1)
{
*pCharOut = *pCharIn;
if (*pCharIn == '\0') break;
if (*pCharOut == '/') *pCharOut = '\\';
pCharIn++;
pCharOut++;
}
mp_table->IterateStart();
PreFile *pPre = mp_table->IterateNext();
while(pPre)
{
if (pPre->FileExists(cleaned_name))
{
return true;
}
pPre = mp_table->IterateNext();
}
return false;
}
// returns pointer to data
uint8 *PreMgr::getContainedFileByHandle(PreFile::FileHandle *pHandle)
{
Dbg_AssertPtr(pHandle);
// if we know that the file in question is not in the PRE system,
// then it's a regular file, don't waste time looking for it
if (mp_activeNonPreHandle == pHandle)
return NULL;
if (mp_activeHandle == pHandle)
// mp_activePre will be unchanged
return mp_activeData;
uint8 *pData = NULL;
mp_table->IterateStart();
PreFile *pPre = mp_table->IterateNext();
while(pPre)
{
pData = pPre->GetContainedFileByHandle(pHandle);
if (pData)
{
mp_activePre = pPre;
mp_activeHandle = pHandle;
mp_activeData = pData;
return pData;
}
pPre = mp_table->IterateNext();
}
// obviously this file is not in the PRE system, mark as such
mp_activeNonPreHandle = pHandle;
return NULL;
}
// there's a wrapper around this now, so that we can do
// some memory-context switching
void PreMgr::loadPre(const char *pFilename, bool async, bool dont_assert, bool useBottomUpHeap)
{
#ifdef DVDETH
m_blockPreLoading = true;
#else
m_blockPreLoading = (bool) Script::GetInt("block_pre_loading", false);
#endif // DVDETH
if (m_blockPreLoading)
return;
// Turn off async for platforms that don't support it
if (!File::CAsyncFileLoader::sAsyncSupported())
{
async = false;
}
#ifdef __PRE_ARAM__
Dbg_MsgAssert(!async, ("Async loading not implemented for ARAM transfer"));
#endif
if( !async && Pcm::UsingCD() )
{
Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
return;
}
// Moved this to below the Pcm::UsingCD() call as that is used (bad!!) to turn off
// music and streams.
# ifdef __PLAT_NGPS__
// scePrintf("Loading PRE file %s...\n", pFilename);
# endif
char fullname[256];
sprintf(fullname, "pre\\%s", pFilename);
# ifdef __PLAT_XBOX__
// Replace the .pre extension (if one exists) with .prx for Xbox PRE file.
if( strrchr( pFilename, '.' ) && ( strlen( fullname ) > 4 ))
{
fullname[strlen( fullname ) - 1] = 'x';
}
# endif
# if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
Tmr::Time basetime = Tmr::ElapsedTime(0);
#endif
int file_size;
uint8 *pFile = NULL;
// Try loading asynchronously
if (async)
{
Dbg_MsgAssert(m_num_pending_pre_files < MAX_NUM_ASYNC_LOADS, ("Too many async LoadPre's pending"));
CAsyncFileHandle *p_fileHandle = CAsyncFileLoader::sOpen( fullname, !async );
if (p_fileHandle)
{
Dbg_MsgAssert(strlen(pFilename) < MAX_COMPACT_FILE_NAME, ("Pre file name %s is greater than %d bytes", pFilename, MAX_COMPACT_FILE_NAME - 1));
// Add to pending list
strcpy(m_pending_pre_files[m_num_pending_pre_files].m_file_name, pFilename);
m_pending_pre_files[m_num_pending_pre_files++].mp_file_handle = p_fileHandle;
file_size = p_fileHandle->GetFileSize();
Dbg_MsgAssert(file_size, ("Pre file size is 0"));
Mem::PushMemProfile((char*)fullname);
if (useBottomUpHeap)
{
pFile = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[file_size];
}
else
{
pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
}
Mem::PopMemProfile();
// Set the callback
p_fileHandle->SetCallback(async_callback, (unsigned int) this, (unsigned int) pFile);
// read the file in
p_fileHandle->Read( pFile, 1, file_size );
return;
}
}
// If we got here, we didn't do an async load
file_size = CanFileBeLoadedQuickly( fullname );
if ( file_size )
{
Mem::PushMemProfile((char*)fullname);
if (useBottomUpHeap)
{
pFile = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[file_size];
}
else
{
pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
}
Mem::PopMemProfile();
bool fileLoaded= LoadFileQuicklyPlease( fullname, pFile );
if ( !fileLoaded )
{
printf( "pre file %s failed to load quickly.\n", fullname );
Dbg_MsgAssert( 0,( "Fire Matt - pre file didn't load quickly." ));
}
}
else
{
void *fp = File::Open(fullname, "rb");
if (!fp)
{
// always run the code below if CD build
if (dont_assert || Config::CD())
{
printf("couldn't open %s\n", fullname);
return;
}
Dbg_MsgAssert(0,( "couldn't open %s\n", fullname));
}
#ifdef __PRE_ARAM__
File::Read(&file_size, 4, 1, fp);
Dbg_MsgAssert(file_size > 0,( "%s has incorrect file size\n", fullname));
if (Config::CD())
{
if (file_size <= 0) printf("%s has incorrect file size\n", fullname);
}
// Stream the file into ARAM.
#define PRE_BUFFER_SIZE (16 * 1024)
char p_buffer[PRE_BUFFER_SIZE];
uint32 p_aram;
if (useBottomUpHeap)
{
p_aram = NsARAM::alloc( file_size, NsARAM::BOTTOMUP );
}
else
{
p_aram = NsARAM::alloc( file_size, NsARAM::TOPDOWN );
}
if ( p_aram )
{
int adjust = 4;
uint32 toread = file_size;
uint32 current_aram_offset = p_aram;
while ( toread )
{
uint32 thistime = PRE_BUFFER_SIZE;
if ( toread < PRE_BUFFER_SIZE ) thistime = toread;
File::Read(&p_buffer[adjust], 1, thistime - adjust, fp);
DCFlushRange ( p_buffer, thistime );
NsDMA::toARAM( current_aram_offset, p_buffer, thistime );
toread -= thistime;
current_aram_offset += thistime;
adjust = 0;
}
File::Close(fp);
pFile = (uint8 *)p_aram;
}
#else
file_size = File::GetFileSize(fp);
// Now allocates the .PRE file from the top of the heap, to avoid fragmentation.
Mem::PushMemProfile((char*)fullname);
if (useBottomUpHeap)
{
pFile = new (Mem::Manager::sHandle().BottomUpHeap()) uint8[file_size];
}
else
{
pFile = new (Mem::Manager::sHandle().TopDownHeap()) uint8[file_size];
}
Mem::PopMemProfile();
//uint8 *pFile = new uint8[file_size];
File::Read(pFile, 1, file_size, fp);
int read_file_size = *((int *) pFile);
Dbg_MsgAssert(file_size == read_file_size,( "%s has incorrect file size: %d vs. expected %d\n", fullname, file_size, read_file_size));
if (Config::CD())
{
if (file_size != read_file_size) printf("%s has incorrect file size\n", fullname);
}
File::Close(fp);
#endif // __PRE_ARAM__
}
# if !defined( __PLAT_NGC__ ) || ( defined( __PLAT_NGC__ ) && !defined( __NOPT_FINAL__ ) )
printf("load time for file %s size %d is %d ms\n", pFilename, file_size, (int) Tmr::ElapsedTime(basetime));
#endif
// the PRE file object winds up at the top of the heap, too. This is fine because
// it will be unloaded at the same time as the big file buffer
if (useBottomUpHeap)
{
if (!mp_table->PutItem(pFilename, new (Mem::Manager::sHandle().BottomUpHeap()) PreFile(pFile,useBottomUpHeap)))
Dbg_MsgAssert(0,( "PRE %s loaded twice", pFilename));
}
else
{
if (!mp_table->PutItem(pFilename, new (Mem::Manager::sHandle().TopDownHeap()) PreFile(pFile)))
Dbg_MsgAssert(0,( "PRE %s loaded twice", pFilename));
}
}
// Finishes the loading sequence
void PreMgr::postLoadPre(CAsyncFileHandle *p_file_handle, uint8 *pData, int size)
{
// Find entry in pending list
for (int i = 0; i < m_num_pending_pre_files; i++)
{
if (m_pending_pre_files[i].mp_file_handle == p_file_handle)
{
// the PRE file object winds up at the top of the heap, too. This is fine because
// it will be unloaded at the same time as the big file buffer
if (!mp_table->PutItem(m_pending_pre_files[i].m_file_name, new (Mem::Manager::sHandle().TopDownHeap()) PreFile(pData)))
{
Dbg_MsgAssert(0,( "PRE %s loaded twice", m_pending_pre_files[i].m_file_name));
}
// Copy last one to this position
m_pending_pre_files[i] = m_pending_pre_files[--m_num_pending_pre_files];
return;
}
}
Dbg_MsgAssert(0, ("PreMgr::postLoadPre(): Can't find entry in pending Pre file list"));
}
// Handles all the async callbacks. Makes sure we only do something on the appropriate callback
void PreMgr::async_callback(CAsyncFileHandle *p_file_handle, EAsyncFunctionType function,
int result, unsigned int arg0, unsigned int arg1)
{
if (function == File::FUNC_READ)
{
PreMgr *p_mgr = (PreMgr *) arg0;
p_mgr->postLoadPre(p_file_handle, (uint8 *) arg1, result);
}
}
// Returns point in string where it will fit in compact space
char * PreMgr::getCompactFileName(char *pName)
{
int length = strlen(pName);
if (length < MAX_COMPACT_FILE_NAME)
{
return pName;
}
else
{
int offset = length - (MAX_COMPACT_FILE_NAME - 1);
return pName + offset;
//return (char *) ((int) pName + offset);
}
}
/*****************************************************************************
** Public Functions **
*****************************************************************************/
DefineSingletonClass(PreMgr, "PRE Manager");
bool PreMgr::InPre(const char *pFilename)
{
return mp_table->GetItem( pFilename );
}
void PreMgr::LoadPre(const char *pFilename, bool async, bool dont_assert, bool useBottomUpHeap)
{
// GJ: This function is a wrapper around loadPRE(), to
// make sure that all allocations go on the top-down heap
// K: Unless they want to use the bottom up heap :)
// Needed so that the anims pre can be put on the bottom up, so that the decompressed anims
// can be put on the top-down, then the pre removed without leaving a hole.
if (useBottomUpHeap)
{
//printf("Loading %s to bottom up heap\n",pFilename);
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().BottomUpHeap());
}
else
{
//printf("Loading %s to top down heap\n",pFilename);
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
}
loadPre(pFilename, async, dont_assert, useBottomUpHeap);
Mem::Manager::sHandle().PopContext();
}
// This function exisits for historical reasons
void PreMgr::LoadPrePermanently(const char *pFilename, bool async, bool dont_assert)
{
// Load the pre file...
// This will go on the top-down heap by default
LoadPre(pFilename, async, dont_assert);
}
void PreMgr::UnloadPre(const char *pFilename, bool dont_assert)
{
# ifdef __PLAT_NGPS__
// scePrintf("Unloading PRE file %s\n", pFilename);
# endif
//printf("Unloading PRE file %s\n", pFilename);
if (m_blockPreLoading)
return;
PreFile *pThePre = mp_table->GetItem(pFilename);
if (!pThePre)
{
if (dont_assert) return;
# ifndef __PLAT_NGC__
Dbg_MsgAssert(0,( "PRE file %s not in PRE manager", pFilename));
# endif
}
mp_table->FlushItem(pFilename);
delete pThePre;
}
bool PreMgr::IsLoadPreFinished(const char *pFilename)
{
// If it's in the pending list, it isn't done loading
for (int i = 0; i < m_num_pending_pre_files; i++)
{
if (strcmp(m_pending_pre_files[i].m_file_name, pFilename) == 0)
{
return false;
}
}
Dbg_MsgAssert(InPre(pFilename), ("IsLoadPreFinished(): Can't find Pre file"));
return true;
}
bool PreMgr::AllLoadPreFinished()
{
return m_num_pending_pre_files == 0;
}
void PreMgr::WaitLoadPre(const char *pFilename)
{
while (!IsLoadPreFinished(pFilename))
{
// We got to call this to allow callbacks to come through
CAsyncFileLoader::sWaitForIOEvent(false);
}
}
void PreMgr::WaitAllLoadPre()
{
while (!AllLoadPreFinished())
{
// We got to call this to allow callbacks to come through
CAsyncFileLoader::sWaitForIOEvent(false);
}
}
bool PreMgr::sPreEnabled()
{
return sp_mgr != NULL;
}
bool PreMgr::sPreExecuteSuccess()
{
return s_lastExecuteSuccess;
}
bool PreMgr::pre_fexist(const char *name)
{
Dbg_MsgAssert(name,( "requesting file NULL"));
if (sp_mgr->fileExists(name))
{
# if DEBUG_PRE
printf("PRE: file %s exists\n", name);
# endif
s_lastExecuteSuccess = true;
return true;
}
// if ( Pcm::UsingCD( ) )
// {
// Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
// return false;
// }
return s_lastExecuteSuccess = false;
}
PreFile::FileHandle *PreMgr::pre_fopen(const char *name, const char *access, bool async)
{
Dbg_MsgAssert(name,( "trying to open file NULL"));
PreFile::FileHandle *pHandle = sp_mgr->getContainedFile(name);
if (pHandle)
{
sp_mgr->mp_activePre->Open(async);
// if we are going to write the file, we want to use the regular file system
const char *pChar = access;
bool am_writing = false;
while(*pChar)
{
if (*pChar != 'r' && *pChar != 'b')
am_writing = true;
pChar++;
}
if (am_writing)
{
# ifdef __PLAT_NGPS__
// scePrintf(" writing file %s\n", name);
# endif
// am writing, so we don't need file open in PRE system
sp_mgr->mp_activePre->Close(async);
}
else
{
// we're reading the file from the PRE system
# if DEBUG_PRE
printf("PRE: opened file %s, handle is 0x%x\n", name, (int) pHandle);
# endif
sp_mgr->mp_activePre->Reset();
s_lastExecuteSuccess = true;
return pHandle;
}
}
// // if we get here, we are using the regular file system
// if ( Pcm::UsingCD( ) )
// {
// Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
// return NULL;
// }
s_lastExecuteSuccess = false;
return NULL;
//return pHandle;
}
int PreMgr::pre_fclose(PreFile::FileHandle *fptr, bool async)
{
Dbg_MsgAssert(fptr,( "calling fclose with invalid file ptr"));
uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
if (pData)
{
#if DEBUG_PRE
printf("PRE: closed file, handle 0x%x\n", (int) fptr);
#endif
sp_mgr->mp_activePre->Close(async);
s_lastExecuteSuccess = true;
return 0;
}
s_lastExecuteSuccess = false;
return 0;
}
size_t PreMgr::pre_fread(void *addr, size_t size, size_t count, PreFile::FileHandle *fptr)
{
Dbg_MsgAssert(fptr,( "calling fread with invalid file ptr"));
uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
if (pData)
{
// read from a simulated file stream in PRE file
Dbg_AssertPtr(sp_mgr->mp_activePre);
s_lastExecuteSuccess = true;
return sp_mgr->mp_activePre->Read(addr, size * count);
}
// if ( Pcm::UsingCD( ) )
// {
// Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
// return 0;
// }
s_lastExecuteSuccess = false;
return 0;
}
size_t PreMgr::pre_fwrite(const void *addr, size_t size, size_t count, PreFile::FileHandle *fptr)
{
#ifdef __NOPT_ASSERT__
uint8 *pData =
#endif
sp_mgr->getContainedFileByHandle(fptr);
Dbg_MsgAssert(!pData,( "can't write to a PRE file"));
// if ( Pcm::UsingCD( ) )
// {
// Dbg_MsgAssert( 0,( "File access forbidden while PCM audio is in progress." ));
// return 0;
// }
s_lastExecuteSuccess = false;
return 0;
}
char *PreMgr::pre_fgets(char *buffer, int maxLen, PreFile::FileHandle *fptr)
{
#ifdef __NOPT_ASSERT__
uint8 *pData =
#endif
sp_mgr->getContainedFileByHandle(fptr);
Dbg_MsgAssert(!pData,( "can't do string ops on a PRE file"));
s_lastExecuteSuccess = false;
return NULL;
}
int PreMgr::pre_fputs(const char *buffer, PreFile::FileHandle *fptr)
{
#ifdef __NOPT_ASSERT__
uint8 *pData =
#endif
sp_mgr->getContainedFileByHandle(fptr);
Dbg_MsgAssert(!pData,( "can't do string ops on a PRE file"));
s_lastExecuteSuccess = false;
return 0;
}
int PreMgr::pre_feof(PreFile::FileHandle *fptr)
{
Dbg_MsgAssert(fptr,( "calling feof with invalid file ptr"));
uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
if (pData)
{
Dbg_AssertPtr(sp_mgr->mp_activePre);
s_lastExecuteSuccess = true;
return sp_mgr->mp_activePre->Eof();
}
s_lastExecuteSuccess = false;
return 0;
}
int PreMgr::pre_fseek(PreFile::FileHandle *fptr, long offset, int origin)
{
uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
if (pData)
{
s_lastExecuteSuccess = true;
return sp_mgr->mp_activePre->Seek(offset, origin);
}
Dbg_MsgAssert(!pData,( "seek not supported for PRE file"));
s_lastExecuteSuccess = false;
return 0;
}
int PreMgr::pre_fflush(PreFile::FileHandle *fptr)
{
#ifdef __NOPT_ASSERT__
uint8 *pData =
#endif
sp_mgr->getContainedFileByHandle(fptr);
Dbg_MsgAssert(!pData,( "flush not supported for PRE file"));
s_lastExecuteSuccess = false;
return 0;
}
int PreMgr::pre_ftell(PreFile::FileHandle *fptr)
{
#ifdef __NOPT_ASSERT__
uint8 *pData =
#endif
sp_mgr->getContainedFileByHandle(fptr);
Dbg_MsgAssert(!pData,( "tell supported for PRE file"));
s_lastExecuteSuccess = false;
return 0;
}
int PreMgr::pre_get_file_size(PreFile::FileHandle *fptr)
{
uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
if (pData)
{
s_lastExecuteSuccess = true;
return sp_mgr->mp_activePre->GetFileSize();
}
Dbg_MsgAssert(!pData,( "get_file_size not supported for PRE file"));
s_lastExecuteSuccess = false;
return 0;
}
int PreMgr::pre_get_file_position(PreFile::FileHandle *fptr)
{
uint8 *pData = sp_mgr->getContainedFileByHandle(fptr);
if (pData)
{
s_lastExecuteSuccess = true;
return sp_mgr->mp_activePre->GetFilePosition();
}
Dbg_MsgAssert(!pData,( "get_file_position not supported for PRE file"));
s_lastExecuteSuccess = false;
return 0;
}
// @script | InPreFile |
// @uparm "string" | filename
bool ScriptInPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
const char *pFilename;
pParams->GetText(NONAME, &pFilename, true);
PreMgr* pre_mgr = PreMgr::Instance();
return pre_mgr->InPre(pFilename);
}
// @script | LoadPreFile |
// @uparm "string" | filename
// @flag async | Load Asynchronously
// @flag dont_assert |
// @flag use_bottom_up_heap |
bool ScriptLoadPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
const char *pFilename;
pParams->GetText(NONAME, &pFilename, true);
PreMgr* pre_mgr = PreMgr::Instance();
pre_mgr->LoadPre(pFilename,
pParams->ContainsFlag(CRCD(0x90e07c79,"async")),
pParams->ContainsFlag(CRCD(0x3d92465e,"dont_assert")),
pParams->ContainsFlag(CRCD(0xa44c770f,"use_bottom_up_heap")));
return true;
}
// @script | UnloadPreFile |
// @flag BoardsPre |
// @flag dont_assert |
// @uparm "string" | filename
bool ScriptUnloadPreFile(Script::CStruct *pParams, Script::CScript *pScript)
{
PreMgr* pre_mgr = PreMgr::Instance();
if (pParams->ContainsFlag("BoardsPre"))
{
// Mdl::Skate * pSkate = Mdl::Skate::Instance();
// pSkate->UnloadBoardPreIfPresent(pParams->ContainsFlag("dont_assert"));
printf ("STUBBED: Unload BoardsPre, in ScriptUnloadPreFile\n");
return true;
}
const char *pFilename;
pParams->GetText(NONAME, &pFilename, true);
pre_mgr->UnloadPre(pFilename, pParams->ContainsFlag("dont_assert"));
return true;
}
// @script | IsLoadPreFinished | Returns true if Pre file has finished loading
// @uparm "string" | filename
bool ScriptIsLoadPreFinished(Script::CStruct *pParams, Script::CScript *pScript)
{
PreMgr* pre_mgr = PreMgr::Instance();
const char *pFilename;
pParams->GetText(NONAME, &pFilename, true);
return pre_mgr->IsLoadPreFinished(pFilename);
}
// @script | AllLoadPreFinished | Returns true if all LoadPre commands have completed
bool ScriptAllLoadPreFinished(Script::CStruct *pParams, Script::CScript *pScript)
{
PreMgr* pre_mgr = PreMgr::Instance();
return pre_mgr->AllLoadPreFinished();
}
// @script | WaitLoadPre | Waits for Pre file to finished loading
// @uparm "string" | filename
bool ScriptWaitLoadPre(Script::CStruct *pParams, Script::CScript *pScript)
{
PreMgr* pre_mgr = PreMgr::Instance();
const char *pFilename;
pParams->GetText(NONAME, &pFilename, true);
pre_mgr->WaitLoadPre(pFilename);
return true;
}
// @script | WaitAllLoadPre | Waits for all Pre files to finished loading
bool ScriptWaitAllLoadPre(Script::CStruct *pParams, Script::CScript *pScript)
{
PreMgr* pre_mgr = PreMgr::Instance();
pre_mgr->WaitAllLoadPre();
return true;
}
// if a file is in a pre, then:
// allocate memory for the file
// if file is uncompressed
// copy it down
// else
// decompress
void * PreMgr::LoadFile(const char *pName, int *p_size, void *p_dest)
{
// NOTE: THIS IS JUST CUT AND PASTE FROM Pre::fileExists
Dbg_AssertPtr(pName);
// replace all '/' with '\'
char cleaned_name[128];
const char *pCharIn = pName;
char *pCharOut = cleaned_name;
while (1)
{
*pCharOut = *pCharIn;
if (*pCharIn == '\0') break;
if (*pCharOut == '/') *pCharOut = '\\';
pCharIn++;
pCharOut++;
}
mp_table->IterateStart();
PreFile *pPre = mp_table->IterateNext();
while(pPre)
{
void *p_data = pPre->LoadContainedFile(cleaned_name,p_size, p_dest);
if (p_data)
{
return p_data;
}
pPre = mp_table->IterateNext();
}
return NULL;
}
} // namespace File