mirror of
https://github.com/thug1src/thug.git
synced 2024-12-02 21:07:12 +00:00
393 lines
9.3 KiB
C++
393 lines
9.3 KiB
C++
/* Header file functionality...
|
|
.Hed files that describe the contents of .Wad files
|
|
Written by Ken, stolen by Matt*/
|
|
#include <eetypes.h>
|
|
#include <eekernel.h>
|
|
#include <stdio.h>
|
|
#include <sifdev.h>
|
|
#include <libsdr.h>
|
|
#include <sdrcmd.h>
|
|
#include <sdmacro.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <sif.h>
|
|
#include <sifrpc.h>
|
|
|
|
#include <libcdvd.h>
|
|
|
|
|
|
#include <core/macros.h>
|
|
#include <core/defines.h>
|
|
|
|
#include <gel/scripting/script.h>
|
|
|
|
#include <sys/file/filesys.h>
|
|
#include <sys/file/ngps/hed.h>
|
|
#include <sys/config/config.h>
|
|
|
|
namespace File
|
|
{
|
|
|
|
static void get_checksums( const char * p_name, uint32 * p_dir, uint32 * p_file )
|
|
{
|
|
char name[128];
|
|
char dir[128];
|
|
char file[128];
|
|
uint lp;
|
|
|
|
// Convert / to \\.
|
|
for ( lp = 0; lp <= strlen( p_name ); lp++ )
|
|
{
|
|
name[lp] = p_name[lp];
|
|
if ( name[lp] == '/' ) name[lp] = '\\';
|
|
}
|
|
|
|
// Find the directory name and the filename.
|
|
for ( lp = strlen( name ) - 1; lp >= 0; lp-- )
|
|
{
|
|
if ( name[lp] == '\\' )
|
|
{
|
|
// Found the directory end.
|
|
strcpy( file, &name[lp+1] );
|
|
name[lp] = '\0';
|
|
if ( name[0]=='\\' )
|
|
{
|
|
strcpy( dir, &name[1] );
|
|
}
|
|
else
|
|
{
|
|
strcpy( dir, name );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Create checksums.
|
|
if ( p_dir ) *p_dir = Script::GenerateCRC( dir );
|
|
if ( p_file ) *p_file = Script::GenerateCRC( file );
|
|
}
|
|
|
|
|
|
// Searches the hed file for the filename. If not found, returns NULL.
|
|
SHedFile *FindFileInHed(const char *pFilename, SHed *pHed)
|
|
{
|
|
Dbg_MsgAssert(pFilename,("NULL pFilename"));
|
|
Dbg_MsgAssert(pHed,("NULL pHed"));
|
|
|
|
uint32 check_dir;
|
|
uint32 check_file;
|
|
|
|
get_checksums( pFilename, &check_dir, &check_file );
|
|
|
|
SHed *pHd=pHed;
|
|
while ( pHd->numFiles != 0xffffffff )
|
|
{
|
|
if ( pHd->Checksum == check_dir )
|
|
{
|
|
SHedFile * pF = pHd->p_fileList;
|
|
for ( uint lp2 = 0; lp2 < pHd->numFiles; lp2++ )
|
|
{
|
|
if ( pF->Checksum == check_file )
|
|
{
|
|
return pF;
|
|
}
|
|
pF++;
|
|
}
|
|
}
|
|
pHd++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Searches the hed file for the filename with the same checksum. If not found, returns NULL.
|
|
SHedFile *FindFileInHedUsingChecksum( uint32 checksum, SHed *pHed )
|
|
{
|
|
// Garrett: This function can take over .1 ms to execute when there are over 2000 entries. We may want to make a better
|
|
// search for this.
|
|
//uint64 start_time = Tmr::GetTimeInUSeconds();
|
|
//int iterations = 0;
|
|
|
|
Dbg_MsgAssert(pHed,("NULL pHed"));
|
|
|
|
SHed *pHd=pHed;
|
|
while ( pHd->numFiles != 0xffffffff )
|
|
{
|
|
SHedFile * pF = pHd->p_fileList;
|
|
for ( uint lp2 = 0; lp2 < pHd->numFiles; lp2++ )
|
|
{
|
|
//iterations++;
|
|
if ( pF->Checksum == checksum )
|
|
{
|
|
//uint64 end_time = Tmr::GetTimeInUSeconds();
|
|
//if ((end_time - start_time) > 100)
|
|
// Dbg_Message("FindNameFromChecksum Found Iterations %d Time %d", iterations, (end_time - start_time));
|
|
return pF;
|
|
}
|
|
pF++;
|
|
}
|
|
pHd++;
|
|
}
|
|
|
|
//uint64 end_time = Tmr::GetTimeInUSeconds();
|
|
//if ((end_time - start_time) > 100)
|
|
// Dbg_Message("FindNameFromChecksum NotFound Iterations %d Time %d", iterations, (end_time - start_time));
|
|
return NULL;
|
|
}
|
|
|
|
extern void StopStreaming( void );
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
// Keep track of the hed buffers so the leak code doesn't think they are leaks
|
|
#define MAX_HED_BUFFERS 3
|
|
uint32 *sp_hed_buffers[MAX_HED_BUFFERS] = { (uint32 *) 16, (uint32 *) 16, (uint32 *) 16 };
|
|
int s_num_hed_buffers = 0;
|
|
#endif // __NOPT_ASSERT__
|
|
|
|
SHed *LoadHed( const char *filename, bool force_cd, bool no_wad )
|
|
{
|
|
int lp;
|
|
uint32 *pHed = NULL;
|
|
|
|
if (Config::CD() || force_cd)
|
|
{
|
|
int HedId=-1;
|
|
char hedFileName[255];
|
|
char uppercaseFilename[ 255 ];
|
|
int sL = strlen( filename );
|
|
Dbg_MsgAssert( sL < 255,( "Filename %s too long", filename ));
|
|
int i;
|
|
for ( i = 0; i < sL; i++ )
|
|
{
|
|
uppercaseFilename[ i ] = toupper( filename[ i ] );
|
|
}
|
|
uppercaseFilename[ i ] = '\0';
|
|
StopStreaming( );
|
|
sprintf( hedFileName, "cdrom0:\\%s%s.HED;1", Config::GetDirectory(),uppercaseFilename );
|
|
int retry = 0;
|
|
while (HedId<0)
|
|
{
|
|
HedId=sceOpen( hedFileName, SCE_RDONLY );
|
|
if (HedId<0)
|
|
{
|
|
printf("Retrying opening %s\n", hedFileName );
|
|
retry++;
|
|
if (retry == 10)
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
printf("%s opened successfully\n", uppercaseFilename );
|
|
long HedSize=sceLseek(HedId, 0, SCE_SEEK_END);
|
|
|
|
Dbg_MsgAssert(pHed==NULL,("pHed not NULL ?"));
|
|
if (no_wad)
|
|
{
|
|
// We'll want to keep this around
|
|
pHed = new (Mem::Manager::sHandle().DebugHeap()) uint32[((HedSize+2047)&~2047) >> 2];
|
|
}
|
|
else
|
|
{
|
|
pHed=(uint32*)Mem::Malloc((HedSize+2047)&~2047);
|
|
}
|
|
Dbg_MsgAssert(pHed,("pHed NULL ?"));
|
|
|
|
/*
|
|
char *p = (char*) pHed;
|
|
// Hmmm.....
|
|
for (int i=0 ;i < (HedSize+2047)&~2047;i++)
|
|
{
|
|
p[i] = 0x55;
|
|
}
|
|
*/
|
|
// 9/5/03 - Garrett: Burn fix. Added retry code to see if HED file actually written.
|
|
// If it cannot, it frees the buffer and returns NULL.
|
|
int read_bytes = 0;
|
|
retry = 0;
|
|
while (read_bytes != HedSize)
|
|
{
|
|
sceLseek(HedId, 0, SCE_SEEK_SET);
|
|
read_bytes = sceRead(HedId,pHed,HedSize);
|
|
if (read_bytes != HedSize)
|
|
{
|
|
printf("READ ERROR: Retrying read of %s\n", hedFileName );
|
|
retry++;
|
|
if (retry == 4)
|
|
{
|
|
printf("READ ERROR: aborting read of %s\n", hedFileName );
|
|
delete pHed;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
sceClose(HedId);
|
|
}
|
|
else
|
|
{
|
|
char tempFileName[ 255];
|
|
|
|
// Make filename
|
|
if (no_wad)
|
|
{
|
|
sprintf( tempFileName, "host:%shost.HED", filename );
|
|
}
|
|
else
|
|
{
|
|
sprintf( tempFileName, "host:%s.HED", filename );
|
|
}
|
|
|
|
void *fp = File::Open( tempFileName, "rb" );
|
|
if ( !fp )
|
|
{
|
|
// Mick: Don't assert, as we want to be able to run without a .HED on the disk, for music
|
|
// Dbg_MsgAssert( 0,( "Couldn't find hed file %s", tempFileName ));
|
|
printf( "failed to find %s\n", tempFileName );
|
|
return ( NULL );
|
|
}
|
|
int fileSize = File::GetFileSize( fp );
|
|
File::Seek( fp, 0, SEEK_SET );
|
|
|
|
if (no_wad)
|
|
{
|
|
// We'll want to keep this around
|
|
pHed = new (Mem::Manager::sHandle().DebugHeap()) uint32[((fileSize+2047)&~2047) >> 2];
|
|
|
|
#ifdef __NOPT_ASSERT__
|
|
if (s_num_hed_buffers < MAX_HED_BUFFERS)
|
|
{
|
|
sp_hed_buffers[s_num_hed_buffers++] = pHed;
|
|
}
|
|
#endif // __NOPT_ASSERT__
|
|
}
|
|
else
|
|
{
|
|
pHed=(uint32*)Mem::Malloc((fileSize+2047)&~2047);
|
|
}
|
|
Dbg_MsgAssert( pHed,( "Failed to allocate memory for hed %s", tempFileName ));
|
|
|
|
File::Read( pHed, 1, fileSize, fp );
|
|
File::Close( fp );
|
|
}
|
|
|
|
// Convert to more compact list.
|
|
int files = 0;
|
|
int dirs = 0;
|
|
|
|
// First, count how many files we have.
|
|
uint32 * p32 = pHed;
|
|
while ( p32[0] != 0xffffffff )
|
|
{
|
|
files++;
|
|
|
|
int len = strlen( (char *)&p32[2] ) + 1;
|
|
len = ( len + 3 ) & ~3;
|
|
p32 = (uint32*)(((int)&p32[2]) + len);
|
|
}
|
|
|
|
// Build list of checksums.
|
|
uint32 *p_offset = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
|
|
uint32 *p_size = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
|
|
uint32 *dir_check = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
|
|
uint32 *file_check = new (Mem::Manager::sHandle().TopDownHeap()) uint32[files];
|
|
char **p_filename = new (Mem::Manager::sHandle().TopDownHeap()) (char *)[files];
|
|
int count = 0;
|
|
p32 = pHed;
|
|
while ( p32[0] != 0xffffffff )
|
|
{
|
|
p_offset[count] = p32[0];
|
|
p_size[count] = p32[1];
|
|
p_filename[count] = (char *)&p32[2];
|
|
get_checksums( (char *)&p32[2], &dir_check[count], &file_check[count] );
|
|
count++;
|
|
|
|
int len = strlen( (char *)&p32[2] ) + 1;
|
|
len = ( len + 3 ) & ~3;
|
|
p32 = (uint32*)(((int)&p32[2]) + len);
|
|
}
|
|
|
|
// Second, count how many directories we have.
|
|
for ( lp = 0; lp < files; lp++ )
|
|
{
|
|
bool unique = true;
|
|
for ( int lp2 = 0; lp2 < lp; lp2++ )
|
|
{
|
|
if ( dir_check[lp] == dir_check[lp2] )
|
|
{
|
|
unique = false;
|
|
break;
|
|
}
|
|
}
|
|
if ( unique ) dirs++;
|
|
}
|
|
|
|
// Now, allocate final piece of memory.
|
|
int size = ( sizeof( SHed ) * ( dirs + 1 ) ) + ( sizeof( SHedFile ) * files );
|
|
SHed * p_hed = (SHed *)new uint8[size];
|
|
|
|
// Fill in directory entries.
|
|
int dirs_added = 0;
|
|
for ( lp = 0; lp < files; lp++ )
|
|
{
|
|
bool unique = true;
|
|
for ( int lp2 = 0; lp2 < dirs_added; lp2++ )
|
|
{
|
|
if ( p_hed[lp2].Checksum == dir_check[lp] )
|
|
{
|
|
unique = false;
|
|
break;
|
|
}
|
|
}
|
|
if ( unique )
|
|
{
|
|
p_hed[dirs_added].Checksum = dir_check[lp];
|
|
dirs_added++;
|
|
}
|
|
}
|
|
|
|
// Fill in file entries.
|
|
SHedFile * p_hed_file = (SHedFile *)&p_hed[(dirs+1)];
|
|
for ( lp = 0; lp < dirs; lp++ )
|
|
{
|
|
p_hed[lp].p_fileList = p_hed_file;
|
|
int files_this_dir = 0;
|
|
for ( int lp2 = 0; lp2 < files; lp2++ )
|
|
{
|
|
if ( dir_check[lp2] == p_hed[lp].Checksum )
|
|
{
|
|
p_hed_file->FileSize = p_size[lp2];
|
|
if (no_wad)
|
|
{
|
|
p_hed_file->p_filename = p_filename[lp2];
|
|
p_hed_file->FileSize |= SHedFile::mNO_WAD; // This marks that we are using the no_wad version
|
|
}
|
|
else
|
|
{
|
|
p_hed_file->Offset = p_offset[lp2];
|
|
}
|
|
p_hed_file->Checksum = file_check[lp2];
|
|
files_this_dir++;
|
|
p_hed_file++;
|
|
}
|
|
}
|
|
p_hed[lp].numFiles = files_this_dir;
|
|
}
|
|
p_hed[dirs].numFiles = 0xffffffff;
|
|
|
|
// Get rid of temp data.
|
|
delete p_filename;
|
|
delete file_check;
|
|
delete dir_check;
|
|
delete p_size;
|
|
delete p_offset;
|
|
if (!no_wad)
|
|
{
|
|
delete pHed;
|
|
}
|
|
|
|
return p_hed;
|
|
}
|
|
|
|
} // namespace File
|