thug/Code/Sk/Scripting/mcfuncs.cpp
2016-02-14 08:39:12 +11:00

4898 lines
154 KiB
C++
Raw Permalink Blame History

//****************************************************************************
//* MODULE: Sk/Scripting
//* FILENAME: McFuncs.cpp
//* OWNER: Kendall Harrison
//* CREATION DATE: 6/10/2002
//****************************************************************************
// Contains Mem card stuff.
// TODO: Make all local variables conform to the naming convention! Currently they are a mix
// start autoduck documentation
// @DOC cfuncs
// @module cfuncs | None
// @subindex Scripting Database
// @index script | cfuncs
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <sk/scripting/mcfuncs.h>
#include <sys/file/filesys.h>
#include <sys/mcman.h>
#include <sys/config/config.h>
#include <gel/music/music.h>
#include <gel/scripting/checksum.h>
#include <gel/scripting/symboltable.h>
#include <gel/scripting/utils.h>
#include <gel/soundfx/soundfx.h>
#include <sk/objects/records.h>
#include <sk/objects/playerprofilemanager.h>
#include <sk/objects/skatercareer.h>
#include <sk/modules/skate/GoalManager.h>
#include <sk/parkeditor2/parked.h>
#include <gel/scripting/component.h>
#include <sys/replay/replay.h>
#include <gel/objtrack.h>
#include <sk/components/goaleditorcomponent.h>
#include <sk/components/raileditorcomponent.h>
#ifdef __PLAT_NGPS__
#include <libmc.h>
#endif // __PLAT_NGPS__
#ifdef __PLAT_NGC__
#include "sys/ngc/p_file.h"
#include "sys/ngc/p_buffer.h"
#include "sys/ngc/p_dma.h"
#include "sys/ngc/p_aram.h"
bool g_mc_hack = false;
uint32 g_hack_address = 0;
extern char * g_p_buffer;
#endif // __PLAT_NGC__
namespace CFuncs
{
using namespace Script;
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
// Whenever the format of one of these file types has changed such that
// an old save will not load, increment its version number.
/*
// GJ: I moved this to memcard.q, because the artists/designers sometimes
// do things that will require a version change.
#define VERSION_OPTIONSANDPROS 10
#define VERSION_NETWORKSETTINGS 3
#define VERSION_CAS 4
#define VERSION_CAT 1
#define VERSION_PARK 2
#define VERSION_REPLAY 6
*/
// The max number of files that can appear in the file list.
// A max is specified both to ensure we don't run out of memory in the files menu, and
// also to limit the pause before the menu comes up because it has to open each file first
// to extract the summary info.
#define MAX_THPS4_FILES_ALLOWED 75
// Limit the space that the summary info structure is allowed to take up when written to a buffer.
// This is because we want to be able to quickly read in the summary info from the start of the
// mem card file without having to read in the whole file. (Units are bytes)
#define MAX_SUMMARY_INFO_SIZE 100
// Max chars allowed in the low level card filename.
#define MAX_CARD_FILE_NAME_CHARS 100
/*****************************************************************************
** Private Types **
*****************************************************************************/
// This value gets added to the character 'a' in the last char of the
// low-level card filename to indicate what the type of the file is.
enum EMemCardFileType
{
MEMCARD_FILETYPE_CAREER,
MEMCARD_FILETYPE_CAS,
MEMCARD_FILETYPE_PARK,
MEMCARD_FILETYPE_REPLAY,
MEMCARD_FILETYPE_NETWORKSETTINGS,
MEMCARD_FILETYPE_CAT,
MEMCARD_FILETYPE_CREATEDGOALS,
};
// The old OptionsAndPros save file now contains the custom skater too, and is divided into 4 parts.
// The LoadFromMemoryCard command provides the option of only applying certain parts to the game state.
// For example, when loading a custom skater, only the CUSTOM_SKATER part is applied, so the career
// remains unaffected.
enum EUnifiedSaveFileSubTypes
{
APPLY_GLOBAL_INFO=0,
APPLY_STORY=1,
APPLY_STORY_SKATER=2,
APPLY_CUSTOM_SKATER=3,
};
#ifdef __PLAT_NGC__
// Icons plus banner.
#define NGC_MEMCARD_ICON_DATA_SIZE ( 64 + ( 32 * 32 * 7 ) + ( 256 * 2 ) + ( 96 * 32 ) + ( 256 * 2 ) )
#endif
// The mem card file starts with one of these, followed by two CStruct's as written
// out using WriteToBuffer.
// The first CStruct is the summary info for the file and occupies no more than
// MAX_SUMMARY_INFO_SIZE bytes.
// The second CStruct holds all the save data.
struct SMcFileHeader
{
#ifdef __PLAT_NGC__
// On the GameCube the icon data is stored within the file rather than as a
// seperate file. So lets wack it into this structure.
uint8 mpIconData[NGC_MEMCARD_ICON_DATA_SIZE];
#endif
#ifdef __PLAT_XBOX__
// I think it is a TRC requirement to use the XBox's special way of calculating
// a checksum. They probably have ways of telling whether we're using it or not.
XCALCSIG_SIGNATURE mSignature;
#else
uint32 mChecksum;
#endif // #ifdef __PLAT_XBOX__
// This is so that the files menu can see if a file might be bad
// given only the summary info.
uint32 mSummaryInfoChecksum;
int mSummaryInfoSize;
// This is the size of this header, the summary info structure, and
// the main structure, rounded up to the platforms block size.
// So for all file types except replays, this should match the total file size.
// In the case of replays, the replay data comes after that lot.
// mDataSize is needed to indicate the start of the replay data.
int mDataSize;
int mVersion;
};
/*****************************************************************************
** Private Data **
*****************************************************************************/
#ifndef __PLAT_NGC__
static unsigned short s_ascii_special[33][2] = {
{0x8140, 32}, /* */
{0x8149, 33}, /* ! */
{0x8168, 34}, /* " */
{0x8194, 35}, /* # */
{0x8190, 36}, /* $ */
{0x8193, 37}, /* % */
{0x8195, 38}, /* & */
{0x8166, 39}, /* ' */
{0x8169, 40}, /* ( */
{0x816a, 41}, /* ) */
{0x8196, 42}, /* * */
{0x817b, 43}, /* + */
{0x8143, 44}, /* , */
{0x817c, 45}, /* - */
{0x8144, 46}, /* . */
{0x815e, 47}, /* / */
{0x8146, 58}, /* : */
{0x8147, 59}, /* ; */
{0x8171, 60}, /* < */
{0x8181, 61}, /* = */
{0x8172, 62}, /* > */
{0x8148, 63}, /* ? */
{0x8197, 64}, /* @ */
{0x816d, 91}, /* [ */
{0x818f, 92}, /* \ */
{0x816e, 93}, /* ] */
{0x814f, 94}, /* ^ */
{0x8151, 95}, /* _ */
{0x8165, 96}, /* ` */
{0x816f, 123}, /* { */
{0x8162, 124}, /* | */
{0x8170, 125}, /* } */
{0x8150, 126}, /* ~ */
};
static unsigned short s_ascii_table[3][2] = {
{0x824f, 0x30}, /* 0-9 */
{0x8260, 0x41}, /* A-Z */
{0x8281, 0x61}, /* a-z */
};
#endif // #ifndef __PLAT_NGC__
// Used by the SaveFailedDueToInsufficientSpace command.
static bool s_insufficient_space=false;
#if __USE_REPLAYS__
static char spReplayCardFileName[MAX_CARD_FILE_NAME_CHARS+1];
static bool sNeedToLoadReplayBuffer=false;
#endif
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
static const char *s_generate_ascii_checksum(char *p_dest, const char *p_string, uint32 fileType=0);
static uint32 s_determine_file_type(char c);
#ifndef __PLAT_NGC__
static unsigned short s_ascii_to_sjis(unsigned char ascii_code);
#endif
static void s_insert_global_info(CStruct *p_struct);
static void s_insert_story_info(CStruct *p_struct);
static void s_insert_story_skater_info(CStruct *p_struct);
static void s_insert_custom_skater_info(CStruct *p_struct);
static void s_insert_game_save_info(uint32 fileType, CStruct *p_struct);
static void s_generate_summary_info(CStruct *p_summaryInfo, uint32 fileType, CStruct *p_mainData);
static void s_read_global_info(CStruct *p_globalInfo, CScript *p_script);
static void s_read_story_info(CStruct *p_storyInfo);
static void s_read_story_skater_info(CStruct *p_storySkaterInfo, CStruct *p_customSkater);
static void s_read_custom_skater_info(CStruct *p_customSkater);
static void s_read_game_save_info(uint32 fileType, CStruct *p_struct, CScript *p_script);
static int s_get_icon_k_required(uint32 fileType);
static int s_calculate_total_space_used_on_card(uint32 fileType, int fileSize);
static int s_get_platforms_block_size();
static int s_round_up_to_platforms_block_size(int fileSize);
static int s_get_version_number(uint32 fileType);
static const char *s_generate_xbox_directory_name(uint32 fileType, const char *p_name);
static void s_generate_card_directory_name(uint32 fileType, const char *p_name, char *p_card_directory_name);
static bool s_make_xbox_dir_and_icons( Mc::Card *p_card,
uint32 fileType, const char *p_name,
char *p_card_file_name,
bool *p_insufficientSpace);
static bool s_make_ps2_dir_and_icons( Mc::Card *p_card,
uint32 fileType, const char *p_name,
char *p_card_file_name,
bool *p_insufficientSpace);
static bool s_insert_ngc_icon( SMcFileHeader *p_fileHeader,
Mc::Card *p_card,
Mc::File *p_file,
uint32 fileType,
const char *p_name);
static uint32 sGetFixedFileSize(uint32 fileType);
// When transferring data between the online vault and the mem card, this structure temporarily holds
// the data. The LoadFromMemoryCard and SaveToMemoryCard commands can be made to load to/from this structure
// instead, so that data can be transferred without loading it into the game.
static Script::CStruct *spVaultData=NULL;
static uint32 sVaultDataType=0;
// Steve's code calls this after downloading the binary data from the vault.
void SetVaultData(uint8 *p_data, uint32 type)
{
if (spVaultData)
{
delete spVaultData;
spVaultData=NULL;
}
spVaultData=new Script::CStruct;
Script::ReadFromBuffer(spVaultData,p_data);
sVaultDataType=type;
}
/*****************************************************************************
** Private Functions **
*****************************************************************************/
/******************************************************************/
/* */
/* */
/******************************************************************/
// This calculates an 8 character ascii checksum of the passed string.
// Used for generating the filename of the memory card file from the name the player has chosen.
// It works by calculating a 32 bit checksum the usual way, then
// converting this number to base 26 and using 'a' to represent 0, 'b' for 1, etc.
// Log to base 26 of 2^32 is 6.8, which means at most 7 ascii characters will be needed, so the
// 8th letter in the string will actually always be a. So the last letter is used to indicate the file type.
static const char *s_generate_ascii_checksum(char *p_dest, const char *p_string, uint32 fileType)
{
Dbg_MsgAssert(p_dest,("NULL p_dest"));
uint32 Checksum=Script::GenerateCRC(p_string);
for (int i=0; i<8; ++i)
{
int Rem=Checksum%26;
p_dest[i]='a'+Rem;
Checksum=(Checksum-Rem)/26;
}
// Check that mathematics is still working ok
Dbg_MsgAssert(Checksum==0,("Checksum not zero ???"));
Dbg_MsgAssert(p_dest[7]=='a',("Last letter of ascii checksum not 'a' ???"));
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
p_dest[7]='a'+MEMCARD_FILETYPE_CAREER;
break;
case 0xffc529f4: // Cas
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
//p_dest[7]='a'+MEMCARD_FILETYPE_CAS;
break;
case 0x61a1bc57: // CAT
p_dest[7]='a'+MEMCARD_FILETYPE_CAT;
break;
case 0x3bf882cc: // Park
p_dest[7]='a'+MEMCARD_FILETYPE_PARK;
break;
case 0x26c80b0d: // Replay
p_dest[7]='a'+MEMCARD_FILETYPE_REPLAY;
break;
case 0xca41692d: // NetworkSettings
p_dest[7]='a'+MEMCARD_FILETYPE_NETWORKSETTINGS;
break;
case 0x62896edf: // CreatedGoals
p_dest[7]='a'+MEMCARD_FILETYPE_CREATEDGOALS;
break;
default:
break;
}
p_dest[8]=0;
return p_dest;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static uint32 s_determine_file_type(char c)
{
switch (c-'a')
{
case MEMCARD_FILETYPE_CAREER:
return 0xb010f357; // OptionsAndPros
break;
case MEMCARD_FILETYPE_CAS:
return 0xffc529f4; // Cas
break;
case MEMCARD_FILETYPE_CAT:
return 0x61a1bc57; // CAT
break;
case MEMCARD_FILETYPE_PARK:
return 0x3bf882cc; // Park
break;
case MEMCARD_FILETYPE_REPLAY:
return 0x26c80b0d; // Replay
break;
case MEMCARD_FILETYPE_NETWORKSETTINGS:
return 0xca41692d; // NetworkSettings
break;
case MEMCARD_FILETYPE_CREATEDGOALS:
return 0x62896edf; // CreatedGoals
break;
default:
return 0;
break;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
#ifndef __PLAT_NGC__
static unsigned short s_ascii_to_sjis(unsigned char ascii_code)
{
if (Config::PAL())
{
// The PS2 shell will not display underscores (it shows them as spaces)
// so convert them to hyphens.
if (ascii_code=='_')
{
ascii_code='-';
}
}
switch ((char)ascii_code)
{
case '<EFBFBD>': ascii_code='B'; break;
case '<EFBFBD>': ascii_code='A'; break;
case '<EFBFBD>': ascii_code='U'; break;
case '<EFBFBD>': ascii_code='O'; break;
case '<EFBFBD>': ascii_code='a'; break;
case '<EFBFBD>': ascii_code='a'; break;
case '<EFBFBD>': ascii_code='a'; break;
case '<EFBFBD>': ascii_code='e'; break;
case '<EFBFBD>': ascii_code='e'; break;
case '<EFBFBD>': ascii_code='e'; break;
case '<EFBFBD>': ascii_code='e'; break;
case '<EFBFBD>': ascii_code='i'; break;
case '<EFBFBD>': ascii_code='i'; break;
case '<EFBFBD>': ascii_code='i'; break;
case '<EFBFBD>': ascii_code='o'; break;
case '<EFBFBD>': ascii_code='o'; break;
case '<EFBFBD>': ascii_code='o'; break;
case '<EFBFBD>': ascii_code='u'; break;
case '<EFBFBD>': ascii_code='u'; break;
case '<EFBFBD>': ascii_code='u'; break;
case '<EFBFBD>': ascii_code='c'; break;
case '<EFBFBD>': ascii_code='o'; break;
default: break;
}
unsigned short sjis_code = 0;
unsigned char stmp=0;
unsigned char stmp2 = 0;
if((ascii_code >= 0x20) && (ascii_code <= 0x2f))
stmp2 = 1;
else if((ascii_code >= 0x30) && (ascii_code <= 0x39))
stmp = 0;
else if((ascii_code >= 0x3a) && (ascii_code <= 0x40))
stmp2 = 11;
else if((ascii_code >= 0x41) && (ascii_code <= 0x5a))
stmp = 1;
else if((ascii_code >= 0x5b) && (ascii_code <= 0x60))
stmp2 = 37;
else if((ascii_code >= 0x61) && (ascii_code <= 0x7a))
stmp = 2;
else if((ascii_code >= 0x7b) && (ascii_code <= 0x7e))
stmp2 = 63;
else
{
printf("bad ASCII code 0x%x\n", ascii_code);
return 0;
}
if (stmp2)
sjis_code = s_ascii_special[ascii_code - 0x20 - (stmp2 - 1)][0];
else
sjis_code = s_ascii_table[stmp][0] + ascii_code - s_ascii_table[stmp][1];
return sjis_code;
}
#endif // __PLAT_NGC__
static void s_insert_global_info(CStruct *p_struct)
{
// Build the global options structure and insert it into p_struct.
Script::CStruct *pOptions=new Script::CStruct;
// Attach the split screen preferences.
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Prefs::Preferences *pPreferences = pSkate->GetSplitScreenPreferences();
Dbg_MsgAssert(pPreferences,("NULL split screen pPreferences"));
// Create a new structure, append the data contained in the preferences, then insert the pointer to the
// new structure into pOptions.
Script::CStruct *pTemp=new Script::CStruct;
pTemp->AppendStructure(pPreferences->GetRoot());
pOptions->AddStructurePointer(CRCD(0xf7720c3f,"SplitScreenPreferences"),pTemp);
// Get the sound options and stick them in a structure and add that.
pTemp=new Script::CStruct;
Sfx::CSfxManager * pSfxManager = Sfx::CSfxManager::Instance();
float MainVolume=pSfxManager->GetMainVolume();
pTemp->AddFloat(CRCD(0x6f016dfb,"MainVolume"),MainVolume);
float MusicVolume=Pcm::GetVolume();
pTemp->AddFloat(CRCD(0xabd4a575,"MusicVolume"),MusicVolume);
//uint64 PlayListForbiddenTrackFlags=Pcm::GetPlaylist();
uint64 list1,list2;
Pcm::GetPlaylist(&list1, &list2);
uint32 PlayListForbiddenTrackFlags1=(uint32)(list1>>32);
uint32 PlayListForbiddenTrackFlags2=(uint32)list1;
uint32 PlayListForbiddenTrackFlags3=(uint32)(list2>>32);
uint32 PlayListForbiddenTrackFlags4=(uint32)list2;
pTemp->AddInteger(CRCD(0x595d2c95,"PlayListForbiddenTrackFlags1"),PlayListForbiddenTrackFlags1);
pTemp->AddInteger(CRCD(0xc0547d2f,"PlayListForbiddenTrackFlags2"),PlayListForbiddenTrackFlags2);
pTemp->AddInteger(CRCD(0xb7534db9,"PlayListForbiddenTrackFlags3"),PlayListForbiddenTrackFlags3);
pTemp->AddInteger(CRCD(0x2937d81a,"PlayListForbiddenTrackFlags4"),PlayListForbiddenTrackFlags4);
// current_soundtrack only applies to XBox
if (Config::GetHardware()==Config::HARDWARE_XBOX)
{
pTemp->AddChecksum(CRCD(0xe1f7c4ae,"current_soundtrack"),Script::GetChecksum("current_soundtrack"));
}
if (Pcm::GetRandomMode())
{
pTemp->AddChecksum(NONAME,CRCD(0x31c71b70,"RandomMode"));
}
pOptions->AddStructurePointer(CRCD(0x89eb9738,"SoundOptions"),pTemp);
// Add the controller preferences.
Script::CArray *pControllerPrefs=new Script::CArray;
pControllerPrefs->SetSizeAndType(Mdl::Skate::vMAX_SKATERS, ESYMBOLTYPE_STRUCTURE);
for (int i=0; i<Mdl::Skate::vMAX_SKATERS; ++i)
{
pTemp=new Script::CStruct;
if (pSkate->mp_controller_preferences[i].AutoKickOn)
{
pTemp->AddChecksum(NONAME,CRCD(0x1eef7085,"AutoKickOn"));
}
if (pSkate->mp_controller_preferences[i].SpinTapsOn)
{
pTemp->AddChecksum(NONAME,CRCD(0xa483ba67,"SpinTapsOn"));
}
if (pSkate->mp_controller_preferences[i].VibrationOn)
{
pTemp->AddChecksum(NONAME,CRCD(0x73cee124,"VibrationOn"));
}
pControllerPrefs->SetStructure(i,pTemp);
}
pOptions->AddArrayPointer(CRCD(0x37bdb853,"ControllerPreferences"),pControllerPrefs);
// Insert the taunt stuff
GameNet::Manager * pGamenet = GameNet::Manager::Instance();
pPreferences=pGamenet->GetTauntPreferences();
Dbg_MsgAssert(pPreferences,("NULL taunt pPreferences"));
pOptions->AddStructure(CRCD(0xe62b6586,"Taunts"),pPreferences->GetRoot());
// Now insert the pointer to the newly constucted pOptions into the mem card structure.
p_struct->AddStructurePointer(CRCD(0x2fca0578,"Options"),pOptions);
// save the career (Game progress, flags and gap checklist)
Mdl::Skate::Instance()->GetCareer()->WriteIntoStructure(p_struct);
// Add the game records.
Records::CGameRecords *pGameRecords=pSkate->GetGameRecords();
Dbg_MsgAssert(pGameRecords,("NULL pGameRecords"));
pGameRecords->WriteIntoStructure(p_struct);
// Wack in the pro skater profile info
Script::CStruct *p_pros=new Script::CStruct;
Obj::CPlayerProfileManager* pPlayerProfileManager=pSkate->GetPlayerProfileManager();
Dbg_MsgAssert(pPlayerProfileManager,("NULL pPlayerProfileManager"));
pPlayerProfileManager->AddAllProProfileInfo(p_pros);
p_struct->AddStructurePointer(CRCD(0xc9986baf,"Pros"),p_pros);
// Note: Mustn't delete pOptions or the other structures created above
// since pointers to these have been given to p_struct.
// p_struct will clean them up when it gets deleted.
}
static void s_insert_story_info(CStruct *p_struct)
{
// Save the goal manager parameters.
Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
CStruct *p_goal_manager_params=p_goal_manager->GetGoalManagerParams();
Dbg_MsgAssert(p_goal_manager_params,("NULL p_goal_manager_params"));
p_struct->AddStructure(CRCD(0xac7c2b62,"GoalManagerParams"),p_goal_manager_params);
}
static void s_insert_story_skater_info(CStruct *p_struct)
{
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Obj::CSkater* pSkater = pSkate->GetSkater(0);
Dbg_Assert( pSkater );
pSkater->AddCATInfo(p_struct);
// stat goal status
pSkater->AddStatGoalInfo(p_struct);
// chapter status
pSkater->AddChapterStatusInfo(p_struct);
}
static void s_insert_custom_skater_info(CStruct *p_struct)
{
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Obj::CPlayerProfileManager* pPlayerProfileManager=pSkate->GetPlayerProfileManager();
Dbg_MsgAssert(pPlayerProfileManager,("NULL pPlayerProfileManager"));
pPlayerProfileManager->AddCASProfileInfo(p_struct);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static void s_insert_game_save_info(uint32 fileType, CStruct *p_struct)
{
// WARNING ! WARNING ! WARNING ! WARNING ! WARNING !
// Make sure that no function in here stores any pointers to new CStructs.
// This is because all new CStructs (and CComponents) allocated here will be coming
// off a special pool, to avoid overflowing the regular pool.
Dbg_MsgAssert(p_struct,("NULL p_struct"));
Mdl::Skate * pSkate = Mdl::Skate::Instance();
#ifdef __NOPT_ASSERT__
#if 0 // Actually remove it fully, as the code that uses it had been temp stubbed out,a nd we don't want to confuse the non final build into thinking it's there
Obj::CPlayerProfileManager* pPlayerProfileManager=pSkate->GetPlayerProfileManager();
Dbg_MsgAssert(pPlayerProfileManager,("NULL pPlayerProfileManager"));
#endif
#endif
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
{
Script::CStruct *p_global_info=new Script::CStruct;
s_insert_global_info(p_global_info);
p_struct->AddStructurePointer(CRCD(0xf55cbd13,"GlobalInfo"),p_global_info);
Script::CStruct *p_story=new Script::CStruct;
s_insert_story_info(p_story);
p_struct->AddStructurePointer(CRCD(0x14a9fbc7,"Story"),p_story);
Script::CStruct *p_story_skater=new Script::CStruct;
s_insert_story_skater_info(p_story_skater);
p_struct->AddStructurePointer(CRCD(0xdf2f448,"StorySkater"),p_story_skater);
Script::CStruct *p_custom_skater=new Script::CStruct;
s_insert_custom_skater_info(p_custom_skater);
p_struct->AddStructurePointer(CRCD(0x12bfac82,"CustomSkater"),p_custom_skater);
break;
}
case 0xca41692d: // NetworkSettings
{
// Add the network preferences
GameNet::Manager * pGamenet = GameNet::Manager::Instance();
Prefs::Preferences *pPreferences=pGamenet->GetNetworkPreferences();
p_struct->AppendStructure(pPreferences->GetRoot());
break;
}
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
/*
pPlayerProfileManager->AddCASProfileInfo(p_struct);
Obj::CSkater* pSkater = pSkate->GetSkater(0);
Dbg_Assert( pSkater );
pSkater->AddCATInfo(p_struct);
*/
break;
}
case 0x61a1bc57: // Cat
{
// Can only edit CAT on skater zero!
Obj::CSkater* pSkater = pSkate->GetSkater(0);
Dbg_Assert( pSkater );
// Index is always 0 since that is the only one that can be edited directly.
Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[0];
Dbg_Assert( pCreatedTrick );
//Other params
p_struct->AddStructure( "other_params", pCreatedTrick->mp_other_params );
//Rotation params
p_struct->AddArray( "rotation_info", pCreatedTrick->mp_rotations );
//Animation params
p_struct->AddArray( "animation_info", pCreatedTrick->mp_animations );
break;
}
case 0x3bf882cc: // Park (Save)
{
Ed::CParkManager::Instance()->WriteIntoStructure(p_struct);
break;
}
case 0x26c80b0d: // Replay
{
#if __USE_REPLAYS__
Replay::AddReplayMemCardInfo(p_struct);
#endif
break;
}
case 0x62896edf: // CreatedGoals
{
Obj::CCompositeObject *p_obj=(Obj::CCompositeObject*)Obj::CTracker::Instance()->GetObject(CRCD(0x81f01058,"GoalEditor"));
Dbg_MsgAssert(p_obj,("No GoalEditor object"));
Obj::CGoalEditorComponent *p_goal_editor=GetGoalEditorComponentFromObject(p_obj);
Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
p_goal_editor->WriteIntoStructure(p_struct);
break;
}
default:
{
Dbg_MsgAssert(0,("Bad type of '%s' sent to s_insert_game_save_info",FindChecksumName(fileType)));
break;
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// Inserts summary info for the specified file type into p_struct.
// Summary info is a small amount of info that is written at the start of the mem card file
// so that it can be quickly read out without having to read the whole file.
// The info gets printed at the bottom of the files menu when the highlight is on a file.
static void s_generate_summary_info(CStruct *p_summaryInfo, uint32 fileType, CStruct *p_mainData)
{
// WARNING ! WARNING ! WARNING ! WARNING ! WARNING !
// Make sure that no function in here stores any pointers to new CStructs.
// This is because all new CStructs (and CComponents) allocated here will be coming
// off a special pool, to avoid overflowing the regular pool.
Dbg_MsgAssert(p_summaryInfo,("NULL p_summaryInfo"));
if (p_mainData)
{
// Extract the summary info from the passed p_mainData.
// This is for when the save process is being done on data that got downloaded from the vault.
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
{
Script::CStruct *p_story=NULL;
p_mainData->GetStructure(CRCD(0x14a9fbc7,"Story"),&p_story,Script::ASSERT);
Script::CStruct *p_goal_manager_params=NULL;
p_story->GetStructure(CRCD(0xac7c2b62,"GoalManagerParams"),&p_goal_manager_params,Script::ASSERT);
CStruct *p_more_goal_manager_params=NULL;
p_goal_manager_params->GetStructure(CRCD(0x23d4170a,"GoalManager_Params"),&p_more_goal_manager_params,Script::ASSERT);
int chapter=0;
p_more_goal_manager_params->GetInteger(CRCD(0xf884773c,"CurrentChapter"),&chapter);
p_summaryInfo->AddInteger(CRCD(0xf884773c,"CurrentChapter"),chapter);
// is_male is used by the script upload_content in net.q
Script::CStruct *p_custom_skater=NULL;
p_mainData->GetStructure(CRCD(0x12bfac82,"CustomSkater"),&p_custom_skater,Script::ASSERT);
Script::CStruct *p_custom=NULL;
p_custom_skater->GetStructure(CRCD(0xa7be964,"Custom"),&p_custom,Script::ASSERT);
Script::CStruct *p_info=NULL;
p_custom->GetStructure(CRCD(0x3476cea8,"Info"),&p_info,Script::ASSERT);
int is_male=0;
p_info->GetInteger(CRCD(0x3f813177,"is_male"),&is_male);
p_summaryInfo->AddInteger(CRCD(0x3f813177,"is_male"),is_male);
const char *p_name="";
p_info->GetString(CRCD(0x2ab66cb8,"display_name"),&p_name);
p_summaryInfo->AddString(CRCD(0xa1dc81f9,"name"),p_name);
break;
}
case 0xca41692d: // NetworkSettings
{
p_summaryInfo->AddString("network_id","");
break;
}
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
break;
}
case 0x61a1bc57: // Cat
{
Script::CStruct *p_data=NULL;
p_mainData->GetStructure(CRCD(0x4137cb14,"other_params"),&p_data,Script::ASSERT);
const char *p_name="";
p_data->GetString(CRCD(0xa1dc81f9,"name"),&p_name,Script::ASSERT);
p_summaryInfo->AddString(CRCD(0xa1dc81f9,"name"),p_name);
break;
}
case 0x26c80b0d: // Replay
{
break;
}
case 0x3bf882cc: // Park
{
int num_edited_goals=0;
Script::CArray *p_goals=NULL;
Script::CStruct *p_goals_struct=NULL;
p_mainData->GetStructure(CRCD(0xd8eb825e,"Park_editor_goals"),&p_goals_struct);
if (p_goals_struct)
{
p_goals_struct->GetArray(CRCD(0x38dbe1d0,"Goals"),&p_goals);
if (p_goals)
{
num_edited_goals=p_goals->GetSize();
}
}
p_summaryInfo->AddInteger(CRCD(0xe1ec606f,"num_edited_goals"),num_edited_goals);
int max_players=1;
p_mainData->GetInteger(CRCD(0xb7e39b53,"MaxPlayers"),&max_players);
p_summaryInfo->AddInteger(CRCD(0xb7e39b53,"MaxPlayers"),max_players);
Script::CArray *p_map=NULL;
p_mainData->GetArray(CRCD(0x337c5289,"Park_editor_map"),&p_map,Script::ASSERT);
// Used by the script upload_content in net.q
int num_gaps=0;
int num_metas=0;
uint16 theme=0;
uint32 tod_script=0;
int width=0;
int length=0;
Ed::CParkManager::Instance()->GetSummaryInfoFromBuffer((uint8*)p_map->GetArrayPointer(),&num_gaps,&num_metas,&theme,&tod_script,&width,&length);
p_summaryInfo->AddInteger(CRCD(0xe6121ed0,"num_gaps"),num_gaps);
p_summaryInfo->AddInteger(CRCD(0xfff3dc35,"num_pieces"),num_metas);
p_summaryInfo->AddInteger(CRCD(0x688a18f7,"theme"),theme);
p_summaryInfo->AddChecksum(CRCD(0x4c72ed98,"tod_script"),tod_script);
p_summaryInfo->AddInteger(CRCD(0x73e5bad0,"width"),width);
p_summaryInfo->AddInteger(CRCD(0xfe82614d,"length"),length);
break;
}
case 0x62896edf: // CreatedGoals
{
int num_edited_goals=0;
Script::CArray *p_goals=NULL;
p_mainData->GetArray(CRCD(0x38dbe1d0,"Goals"),&p_goals);
if (p_goals)
{
num_edited_goals=p_goals->GetSize();
}
p_summaryInfo->AddInteger(CRCD(0xe1ec606f,"num_edited_goals"),num_edited_goals);
break;
}
default:
{
Dbg_MsgAssert(0,("Bad type of '%s' sent to s_generate_summary_info",FindChecksumName(fileType)));
break;
}
}
}
else
{
// Get the summary info from the game.
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
{
Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
CStruct *p_goal_manager_params=p_goal_manager->GetGoalManagerParams();
Dbg_MsgAssert(p_goal_manager_params,("NULL p_goal_manager_params"));
// Grab the summary info.
CStruct *p_more_goal_manager_params=NULL;
p_goal_manager_params->GetStructure(CRCD(0x23d4170a,"GoalManager_Params"),&p_more_goal_manager_params);
Dbg_MsgAssert(p_more_goal_manager_params,("No GoalManager_Params structure ??"));
int chapter=0;
p_more_goal_manager_params->GetInteger(CRCD(0xf884773c,"CurrentChapter"),&chapter);
p_summaryInfo->AddInteger(CRCD(0xf884773c,"CurrentChapter"),chapter);
break;
}
case 0xca41692d: // NetworkSettings
{
GameNet::Manager * pGamenet = GameNet::Manager::Instance();
Prefs::Preferences *pPreferences=pGamenet->GetNetworkPreferences();
CStruct *p_net_stuff=pPreferences->GetRoot();
// Grab the summary info.
CStruct *p_foo=NULL;
p_net_stuff->GetStructure("network_id",&p_foo);
Dbg_MsgAssert(p_foo,("No network_id structure ?"));
const char *p_name="";
p_foo->GetString("ui_string",&p_name);
p_summaryInfo->AddString("network_id",p_name);
break;
}
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
break;
}
case 0x61a1bc57: // Cat
{
// Can only edit CAT on skater zero!
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Obj::CSkater* pSkater = pSkate->GetSkater(0);
Dbg_Assert( pSkater );
// Index is always 0 since that is the only one that can be edited directly.
Game::CCreateATrick* pCreatedTrick = pSkater->m_created_trick[0];
Dbg_Assert( pCreatedTrick );
const char *p_name="";
pCreatedTrick->mp_other_params->GetString(CRCD(0xa1dc81f9,"name"),&p_name,Script::ASSERT);
p_summaryInfo->AddString(CRCD(0xa1dc81f9,"name"),p_name);
break;
}
case 0x26c80b0d: // Replay
{
#if __USE_REPLAYS__
Replay::AddReplayMemCardSummaryInfo(p_summaryInfo);
#endif
break;
}
case 0x3bf882cc: // Park
case 0x62896edf: // CreatedGoals
{
Obj::CCompositeObject *p_obj=(Obj::CCompositeObject*)Obj::CTracker::Instance()->GetObject(CRCD(0x81f01058,"GoalEditor"));
Dbg_MsgAssert(p_obj,("No GoalEditor object"));
Obj::CGoalEditorComponent *p_goal_editor=GetGoalEditorComponentFromObject(p_obj);
Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
p_summaryInfo->AddInteger(CRCD(0xe1ec606f,"num_edited_goals"),p_goal_editor->GetNumGoals());
if (fileType==CRCD(0x3bf882cc,"Park"))
{
p_summaryInfo->AddInteger(CRCD(0xb7e39b53,"MaxPlayers"),Ed::CParkManager::Instance()->GetGenerator()->GetMaxPlayers());
int num_gaps=0;
int num_metas=0;
uint16 theme=0;
uint32 tod_script=0;
int width=0;
int length=0;
Ed::CParkManager::Instance()->WriteCompressedMapBuffer(); // Ensure map buffer is correct
Ed::CParkManager::Instance()->GetSummaryInfoFromBuffer(Ed::CParkManager::Instance()->GetCompressedMapBuffer(),&num_gaps,&num_metas,&theme,&tod_script,&width,&length);
p_summaryInfo->AddInteger(CRCD(0xe6121ed0,"num_gaps"),num_gaps);
p_summaryInfo->AddInteger(CRCD(0xfff3dc35,"num_pieces"),num_metas);
p_summaryInfo->AddInteger(CRCD(0x688a18f7,"theme"),theme);
p_summaryInfo->AddChecksum(CRCD(0x4c72ed98,"tod_script"),tod_script);
p_summaryInfo->AddInteger(CRCD(0x73e5bad0,"width"),width);
p_summaryInfo->AddInteger(CRCD(0xfe82614d,"length"),length);
}
break;
}
default:
{
Dbg_MsgAssert(0,("Bad type of '%s' sent to s_generate_summary_info",FindChecksumName(fileType)));
break;
}
}
}
}
static void s_read_global_info(CStruct *p_globalInfo, CScript *p_script)
{
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Script::CStruct *pOptions=NULL;
p_globalInfo->GetStructure(CRCD(0x2fca0578,"Options"),&pOptions);
Dbg_MsgAssert(pOptions,("p_globalInfo is missing Options structure"));
// Extract the split screen preferences
Script::CStruct *pTemp=NULL;
pOptions->GetStructure(CRCD(0xf7720c3f,"SplitScreenPreferences"),&pTemp);
Prefs::Preferences *pPreferences = pSkate->GetSplitScreenPreferences();
Dbg_MsgAssert(pPreferences,("NULL split screen pPreferences"));
pPreferences->SetRoot(pTemp);
// Get the taunt preferences
pTemp=NULL;
pOptions->GetStructure(CRCD(0xe62b6586,"Taunts"),&pTemp);
GameNet::Manager * pGamenet = GameNet::Manager::Instance();
pPreferences=pGamenet->GetTauntPreferences();
Dbg_MsgAssert(pPreferences,("NULL taunt pPreferences"));
pPreferences->SetRoot(pTemp);
// Get the sound options.
pOptions->GetStructure(CRCD(0x89eb9738,"SoundOptions"),&pTemp);
Sfx::CSfxManager * pSfxManager = Sfx::CSfxManager::Instance();
float MainVolume=0;
if (pTemp->GetFloat(CRCD(0x6f016dfb,"MainVolume"),&MainVolume))
{
pSfxManager->SetMainVolume(MainVolume);
}
float MusicVolume=0;
if (pTemp->GetFloat(CRCD(0xabd4a575,"MusicVolume"),&MusicVolume))
{
Pcm::SetVolume(MusicVolume);
}
uint64 PlayListForbiddenTrackFlagsA=0;
uint64 PlayListForbiddenTrackFlagsB=0;
uint32 PlayListForbiddenTrackFlags1=0;
uint32 PlayListForbiddenTrackFlags2=0;
uint32 PlayListForbiddenTrackFlags3=0;
uint32 PlayListForbiddenTrackFlags4=0;
if (pTemp->GetInteger(CRCD(0x595d2c95,"PlayListForbiddenTrackFlags1"),(int*)&PlayListForbiddenTrackFlags1))
{
pTemp->GetInteger(CRCD(0xc0547d2f,"PlayListForbiddenTrackFlags2"),(int*)&PlayListForbiddenTrackFlags2);
pTemp->GetInteger(CRCD(0xb7534db9,"PlayListForbiddenTrackFlags3"),(int*)&PlayListForbiddenTrackFlags3);
pTemp->GetInteger(CRCD(0x2937d81a,"PlayListForbiddenTrackFlags4"),(int*)&PlayListForbiddenTrackFlags4);
PlayListForbiddenTrackFlagsA=(((uint64)PlayListForbiddenTrackFlags1)<<32)+PlayListForbiddenTrackFlags2;
PlayListForbiddenTrackFlagsB=(((uint64)PlayListForbiddenTrackFlags3)<<32)+PlayListForbiddenTrackFlags4;
Pcm::SetPlaylist(PlayListForbiddenTrackFlagsA, PlayListForbiddenTrackFlagsB);
}
if (pTemp->ContainsFlag(CRCD(0x31c71b70,"RandomMode")))
{
Pcm::SetRandomMode(true);
}
else
{
Pcm::SetRandomMode(false);
}
if (Config::GetHardware()==Config::HARDWARE_XBOX)
{
uint32 current_soundtrack=0xffffffff;
pTemp->GetChecksum(CRCD(0xe1f7c4ae,"current_soundtrack"),&current_soundtrack);
Script::CSymbolTableEntry *p_sym=Script::LookUpSymbol(0xe1f7c4ae/*current_soundtrack*/);
if (p_sym)
{
Dbg_MsgAssert(p_sym->mType==ESYMBOLTYPE_NAME,("Expected current_soundtrack to have type checksum"));
p_sym->mChecksum=current_soundtrack;
}
Script::CStruct* pTemp2 = new Script::CStruct;
pTemp2->AddChecksum(CRCD(0x8c465905,"trackchecksum"),current_soundtrack);
Script::RunScript(CRCD(0xd44400c2,"set_loaded_soundtrack"), pTemp2);
}
// Extract the controller preferences
Script::CArray *pControllerPrefs=NULL;
if (pOptions->GetArray(CRCD(0x37bdb853,"ControllerPreferences"),&pControllerPrefs))
{
for (int i=0; i<Mdl::Skate::vMAX_SKATERS; ++i)
{
Script::CStruct *pPrefs=pControllerPrefs->GetStructure(i);
Dbg_MsgAssert(pPrefs,("NULL pPrefs?"));
pSkate->SetVibration(i,pPrefs->ContainsFlag(CRCD(0x73cee124,"VibrationOn")));
pSkate->SetAutoKick(i,pPrefs->ContainsFlag(CRCD(0x1eef7085,"AutoKickOn")));
pSkate->SetSpinTaps(i,pPrefs->ContainsFlag(CRCD(0xa483ba67,"SpinTapsOn")));
}
}
// Extract the game records.
Records::CGameRecords *pGameRecords=pSkate->GetGameRecords();
Dbg_MsgAssert(pGameRecords,("NULL pGameRecords"));
pGameRecords->ReadFromStructure(p_globalInfo);
// Extract the pro & cas skater profile info
Script::CStruct *p_pros=NULL;
p_globalInfo->GetStructure(CRCD(0xc9986baf,"Pros"),&p_pros,Script::ASSERT);
Obj::CPlayerProfileManager* pPlayerProfileManager=pSkate->GetPlayerProfileManager();
pPlayerProfileManager->LoadAllProProfileInfo(p_pros);
// Extract the career info (Game progress, flags and gap checklist)
Mdl::Skate::Instance()->GetCareer()->ReadFromStructure(p_globalInfo);
// Return the LastLevelLoadScript and LastGameMode to the calling script.
// Needed by Zac so that after autoloading the game can automatically load up the
// last level that was being played when saved.
uint32 last_level_load_script=0;
uint32 last_game_mode=0;
CStruct *p_career=NULL;
int current_theme=0;
p_globalInfo->GetStructure(CRCD(0x4da4937b,"Career"),&p_career);
if (p_career)
{
p_career->GetChecksum(CRCD(0xe3335d2f,"LastLevelLoadScript"),&last_level_load_script);
p_career->GetChecksum(CRCD(0x2cc06f5e,"LastGameMode"),&last_game_mode);
// Extract current theme
p_career->GetInteger(CRCD(0xcc946ff3,"current_theme"),&current_theme);
}
if (last_level_load_script)
{
p_script->GetParams()->AddChecksum(CRCD(0xe3335d2f,"LastLevelLoadScript"),last_level_load_script);
}
if (last_game_mode)
{
p_script->GetParams()->AddChecksum(CRCD(0x2cc06f5e,"LastGameMode"),last_game_mode);
}
//if (current_theme) TT7448: current_theme can be saved as zero
//{
Script::CStruct* pTemp2 = new Script::CStruct;
pTemp2->AddInteger(CRCD(0x3bed2cf5,"theme_num"),current_theme);
pTemp2->AddChecksum(CRCD(0x476cdadd,"loading_career"),0);
Script::RunScript( "set_current_theme", pTemp2 );
delete pTemp2;
// }
// Now, if the skater is custom, set his cas file name.
Obj::CSkaterProfile* pSkaterProfile=pSkate->GetCurrentProfile();
if (!pSkaterProfile->IsPro())
{
const char *pCASFileName="Unimplemented";
p_globalInfo->GetString(CRCD(0xf36c1878,"CASFileName"),&pCASFileName);
pSkaterProfile->SetCASFileName(pCASFileName);
}
}
static void s_read_story_info(CStruct *p_storyInfo)
{
// Load in the goal manager parameters.
CStruct *p_loaded_goal_manager_params=NULL;
p_storyInfo->GetStructure(CRCD(0xac7c2b62,"GoalManagerParams"),&p_loaded_goal_manager_params);
Dbg_MsgAssert(p_loaded_goal_manager_params,("No goal manager params on mem card"));
Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
p_goal_manager->ResetCareer();
CStruct *p_goal_manager_params=p_goal_manager->GetGoalManagerParams();
Dbg_MsgAssert(p_goal_manager_params,("NULL p_goal_manager_params"));
p_goal_manager_params->Clear();
p_goal_manager_params->AppendStructure(p_loaded_goal_manager_params);
// Refresh the goal manager with the new params.
p_goal_manager->LevelLoad();
}
static void s_read_story_skater_info(CStruct *p_storySkaterInfo, CStruct *p_customSkater)
{
// Which skater are we loading?
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Obj::CPlayerProfileManager* pPlayerProfileManager=pSkate->GetPlayerProfileManager();
int index = pPlayerProfileManager->GetCurrentProfileIndex();
Dbg_MsgAssert(index==0,("Should not be loading story skater info into any skater other than skater 0, tried to load it into skater %d",index));
Obj::CSkater* pSkater = pSkate->GetSkater(index);
Dbg_Assert( pSkater );
pSkater->LoadCATInfo(p_storySkaterInfo);
pSkater->LoadStatGoalInfo(p_storySkaterInfo);
pSkater->LoadChapterStatusInfo(p_storySkaterInfo);
pPlayerProfileManager->LoadCASProfileInfo(p_customSkater, true);
}
static void s_read_custom_skater_info(CStruct *p_customSkater)
{
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Obj::CPlayerProfileManager* pPlayerProfileManager=pSkate->GetPlayerProfileManager();
int index = pPlayerProfileManager->GetCurrentProfileIndex();
if ( index == 0 )
{
// if this is player one... don't apply the stats
pPlayerProfileManager->LoadCASProfileInfo(p_customSkater, false);
}
else
{
// if this is any other skater, then apply the stats
pPlayerProfileManager->LoadCASProfileInfo(p_customSkater, true);
}
}
uint32 s_apply_flags=0;
bool s_did_apply_custom_skater_info=false;
static void s_read_game_save_info(uint32 fileType, CStruct *p_struct, CScript *p_script)
{
Dbg_MsgAssert(p_struct,("NULL p_struct"));
Dbg_MsgAssert(p_script,("NULL p_script"));
s_did_apply_custom_skater_info=false;
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
{
Dbg_MsgAssert(s_apply_flags,("s_apply_flags is zero, need to call SetSectionsToApplyWhenLoading"));
if (s_apply_flags & (1<<APPLY_GLOBAL_INFO))
{
printf("APPLY_GLOBAL_INFO\n");
Script::CStruct *p_global_info=NULL;
p_struct->GetStructure(CRCD(0xf55cbd13,"GlobalInfo"),&p_global_info,Script::ASSERT);
s_read_global_info(p_global_info, p_script);
}
if (s_apply_flags & (1<<APPLY_STORY))
{
// Must do this before the story skater is loaded
// so that the proper difficulty is set
// when the stat goals get loaded
printf("APPLY_STORY\n");
Script::CStruct *p_story_info=NULL;
p_struct->GetStructure(CRCD(0x14a9fbc7,"Story"),&p_story_info,Script::ASSERT);
s_read_story_info(p_story_info);
}
if (s_apply_flags & (1<<APPLY_CUSTOM_SKATER))
{
printf("APPLY_CUSTOM_SKATER\n");
Script::CStruct *p_custom_skater_info=NULL;
p_struct->GetStructure(CRCD(0x12bfac82,"CustomSkater"),&p_custom_skater_info,Script::ASSERT);
s_read_custom_skater_info(p_custom_skater_info);
s_did_apply_custom_skater_info=true;
}
if (s_apply_flags & (1<<APPLY_STORY_SKATER))
{
printf("APPLY_STORY_SKATER\n");
Script::CStruct *p_story_skater_info=NULL;
p_struct->GetStructure(CRCD(0xdf2f448,"StorySkater"),&p_story_skater_info,Script::ASSERT);
Script::CStruct *p_custom_skater_info=NULL;
p_struct->GetStructure(CRCD(0x12bfac82,"CustomSkater"),&p_custom_skater_info,Script::ASSERT);
s_read_story_skater_info(p_story_skater_info,p_custom_skater_info);
}
// Reset the flags to that the above assert will catch cases where the scripts
// have not called SetSectionsToApplyWhenLoading
s_apply_flags=0;
break;
}
case 0xca41692d: // NetworkSettings
{
// Extract the network preferences
GameNet::Manager * pGamenet = GameNet::Manager::Instance();
Prefs::Preferences *pPreferences=pGamenet->GetNetworkPreferences();
Dbg_MsgAssert(pPreferences,("NULL network pPreferences"));
pPreferences->SetRoot(p_struct);
break;
}
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
/*
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Obj::CPlayerProfileManager* pPlayerProfileManager=pSkate->GetPlayerProfileManager();
//Script::PrintContents( p_struct );
pPlayerProfileManager->LoadCASProfileInfo(p_struct);
// Which skater are we loading?
int index = pPlayerProfileManager->GetCurrentProfileIndex();
Obj::CSkater* pSkater = pSkate->GetSkater(index);
Dbg_Assert( pSkater );
pSkater->LoadCATInfo(p_struct);
*/
break;
}
case 0x61a1bc57: // Cat
{
Mdl::Skate * pSkate = Mdl::Skate::Instance();
Script::CStruct *p_other_params;
Script::CArray *p_rotation_info;
Script::CArray *p_animation_info;
Obj::CSkater* pSkater = pSkate->GetLocalSkater();
if ( pSkater )
{
//Params
p_struct->GetStructure( "other_params", &p_other_params, Script::ASSERT );
//pSkater->m_created_trick[0]->mp_other_params = p_other_params;
pSkater->m_created_trick[0]->mp_other_params->AppendStructure( p_other_params );
//Rotations
p_struct->GetArray( "rotation_info", &p_rotation_info, Script::ASSERT );
Script::CopyArray( pSkater->m_created_trick[0]->mp_rotations, p_rotation_info );
//Animations
p_struct->GetArray( "animation_info", &p_animation_info, Script::ASSERT );
Script::CopyArray( pSkater->m_created_trick[0]->mp_animations, p_animation_info );
}
break;
}
case 0x3bf882cc: // Park (read save game info)
{
Ed::CParkManager::Instance()->ReadFromStructure(p_struct);
break;
}
case 0x26c80b0d: // Replay
{
#if __USE_REPLAYS__
uint32 level_structure_name=0;
p_struct->GetChecksum("level_structure_name",&level_structure_name);
Replay::SetLevelStructureName(level_structure_name);
// Look up the load_script in the level structure, and return it to the calling
// script so that after calling LoadFromMemoryCard the script can call the load_script,
// which will load the appropriate level.
CStruct *p_level_structure=GetStructure(level_structure_name);
Dbg_MsgAssert(p_level_structure,("Could not find level structure '%s'",FindChecksumName(level_structure_name)));
uint32 load_script=0;
p_level_structure->GetChecksum("load_script",&load_script);
p_script->GetParams()->AddChecksum("load_script",load_script);
#endif
break;
}
case 0x62896edf: // CreatedGoals
{
Obj::CCompositeObject *p_obj=(Obj::CCompositeObject*)Obj::CTracker::Instance()->GetObject(CRCD(0x81f01058,"GoalEditor"));
Dbg_MsgAssert(p_obj,("No GoalEditor object"));
Obj::CGoalEditorComponent *p_goal_editor=GetGoalEditorComponentFromObject(p_obj);
Dbg_MsgAssert(p_goal_editor,("No goal editor component ???"));
p_goal_editor->ReadFromStructure(p_struct);
break;
}
default:
{
Dbg_MsgAssert(0,("Bad type of '%s' sent to s_read_game_save_info",FindChecksumName(fileType)));
break;
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int s_get_icon_k_required(uint32 fileType)
{
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
return Script::GetInteger(CRCD(0x4925ed9e,"OptionsProsIconSpaceRequired"));
break;
case 0xca41692d: // NetworkSettings
return Script::GetInteger(CRCD(0xf3431f63,"NetworkSettingsIconSpaceRequired"));
break;
case 0xffc529f4: // Cas
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
//return Script::GetInteger("CASIconSpaceRequired");
break;
case 0x61a1bc57: // Cat
return Script::GetInteger(CRCD(0x46b0ef40,"CATIconSpaceRequired"));
break;
case 0x3bf882cc: // Park
return Script::GetInteger(CRCD(0x82aaf8dc,"ParkIconSpaceRequired"));
break;
case 0x26c80b0d: // Replay
return Script::GetInteger(CRCD(0x7eaf934f,"ReplayIconSpaceRequired"));
break;
case 0x62896edf: // CreatedGoals
return Script::GetInteger(CRCD(0x3205bc07,"CreatedGoalsIconSpaceRequired"));
break;
default:
Dbg_MsgAssert(0,("Bad fileType of '%s' sent to s_get_icon_k_required",FindChecksumName(fileType)));
break;
}
return 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int s_calculate_total_space_used_on_card(uint32 fileType, int fileSize)
{
int blocks=s_round_up_to_platforms_block_size(fileSize)/s_get_platforms_block_size();
switch (Config::GetHardware())
{
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
{
// Add in the icon space required
blocks+=s_get_icon_k_required(fileType);
// Then add in the space used up by the mem card file system.
blocks+=4;
break;
}
case Config::HARDWARE_XBOX:
{
// Add in the icon space required
blocks+=3;
break;
}
case Config::HARDWARE_NGC:
// All done, I think.
break;
default:
break;
}
return blocks;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// This returns the block size for mem card files.
// On some platforms, using a library function to get the file size will not
// return the original file size, but will return the size rounded up to the next
// multiple of the block size.
// In order that the calculated checksum be the same on loading, the files are always
// padded with zeros to be a multiple of the block size before the checksum is calculated.
static int s_get_platforms_block_size()
{
switch (Config::GetHardware())
{
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
return 1024;
break;
case Config::HARDWARE_NGC:
return 8192;
break;
case Config::HARDWARE_XBOX:
return 16384;
break;
default:
break;
}
return 1;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int s_round_up_to_platforms_block_size(int fileSize)
{
int block_size=s_get_platforms_block_size();
if (fileSize%block_size)
{
return (1+fileSize/block_size)*block_size;
}
else
{
return fileSize;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static int s_get_version_number(uint32 fileType)
{
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
return Script::GetInt( "VERSION_OPTIONSANDPROS", Script::ASSERT );
break;
case 0xffc529f4: // Cas
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
//return Script::GetInt( "VERSION_CAS", Script::ASSERT );
break;
case 0x61a1bc57: // Cat
return Script::GetInt( "VERSION_CAT", Script::ASSERT );
break;
case 0x3bf882cc: // Park
return Script::GetInt( "VERSION_PARK", Script::ASSERT );
break;
case 0x26c80b0d: // Replay
return Script::GetInt( "VERSION_REPLAY", Script::ASSERT );
break;
case 0xca41692d: // NetworkSettings
return Script::GetInt( "VERSION_NETWORKSETTINGS", Script::ASSERT );
break;
case 0x62896edf: // CreatedGoals
return Script::GetInt( "VERSION_CREATEDGOALS", Script::ASSERT );
break;
default:
Dbg_MsgAssert(0,("Bad fileType of '%s' sent to s_get_version_number",FindChecksumName(fileType)));
break;
}
return -1;
}
static const char *s_generate_xbox_directory_name(uint32 fileType, const char *p_name)
{
static char p_directory_name[100];
switch( fileType )
{
case 0xca41692d: // NetworkSettings
{
sprintf( p_directory_name, "%s-NetworkSettings", p_name);
break;
}
case 0xb010f357: // OptionsAndPros
{
sprintf( p_directory_name, "%s-Story/Skater", p_name );
break;
}
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
//sprintf( p_directory_name, "%s-Skater", p_name );
break;
}
case 0x61a1bc57: // Cat
{
sprintf( p_directory_name, "%s-Trick", p_name );
break;
}
case 0x3bf882cc: // Park
{
sprintf( p_directory_name, "%s-Park", p_name );
break;
}
case 0x26c80b0d: // Replay
{
sprintf( p_directory_name, "%s-Replay", p_name );
break;
}
case 0x62896edf: // CreatedGoals
{
sprintf( p_directory_name, "%s-Goals", p_name );
break;
}
default:
{
Dbg_MsgAssert( 0, ( "Bad file type of '%s' for file '%s' sent to s_generate_xbox_directory_name", FindChecksumName(fileType),p_name));
break;
}
}
Dbg_MsgAssert( strlen( p_directory_name ) < 100, ( "Oops" ));
return p_directory_name;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// Returns false if error.
static bool s_make_xbox_dir_and_icons( Mc::Card *p_card,
uint32 fileType, const char *p_name,
char *p_card_file_name,
bool *p_insufficientSpace)
{
Dbg_MsgAssert(p_card_file_name,("NULL p_card_file_name"));
p_card_file_name[0]=0;
// Generate the directory name
const char *p_directory_name=s_generate_xbox_directory_name(fileType,p_name);
// Create the directory
if (!p_card->MakeDirectory(p_directory_name))
{
if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
{
*p_insufficientSpace=true;
}
return false;
}
// Calculate the low-level file name.
const char *p_low_level_directory_name=p_card->ConvertDirectory(p_directory_name);
if (!p_low_level_directory_name)
{
return false;
}
sprintf( p_card_file_name, "/%s/%s", p_low_level_directory_name, p_low_level_directory_name );
Dbg_MsgAssert(strlen(p_card_file_name)<MAX_CARD_FILE_NAME_CHARS,("Oops"));
// Write out the icon file.
char pTitle[100];
char* pSourceIconFile = "";
char* pIcoName = "";
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
{
const char *p_title=Script::GetString(CRCD(0x130ab28a,"cfuncs_str_thugstoryskater"));
strcpy(pTitle,p_title);
strcat(pTitle,p_name);
pIcoName = "saveimage.xbx";
pSourceIconFile = "images\\miscellaneous\\xbox_icon_career.xbx";
break;
}
case 0xca41692d: // NetworkSettings
{
const char *p_title=Script::GetString(CRCD(0x92c497ae,"cfuncs_str_thugnetsettings"));
strcpy(pTitle,p_title);
strcat(pTitle,p_name);
pIcoName = "saveimage.xbx";
// Need new icon ...
pSourceIconFile = "images\\miscellaneous\\xbox_icon_career.xbx";
break;
}
case 0x62896edf: // CreatedGoals
{
const char *p_title=Script::GetString(CRCD(0x257678cb,"cfuncs_str_thuggoals"));
strcpy(pTitle,p_title);
strcat(pTitle,p_name);
pIcoName = "saveimage.xbx";
// Need new icon ...
pSourceIconFile = "images\\miscellaneous\\xbox_icon_goal.xbx";
break;
}
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
/*
sprintf(pTitle,"THUG Skater:%s",p_name);
pIcoName = "saveimage.xbx";
pSourceIconFile = "images\\miscellaneous\\xbox_icon_skater.xbx";
*/
break;
}
case 0x61a1bc57: // CAT
{
const char *p_title=Script::GetString(CRCD(0x3aa2cffa,"cfuncs_str_thugtrick"));
strcpy(pTitle,p_title);
strcat(pTitle,p_name);
pIcoName = "saveimage.xbx";
pSourceIconFile = "images\\miscellaneous\\xbox_icon_tricks.xbx";
break;
}
case 0x3bf882cc: // Park
{
const char *p_title=Script::GetString(CRCD(0x2171fddc,"cfuncs_str_thugpark"));
strcpy(pTitle,p_title);
strcat(pTitle,p_name);
pIcoName = "saveimage.xbx";
pSourceIconFile = "images\\miscellaneous\\xbox_icon_park.xbx";
break;
}
case 0x26c80b0d: // Replay
{
//sprintf(pTitle,"THUG Replay:%s",p_name);
//pIcoName = "saveimage.xbx";
//pSourceIconFile = "images\\miscellaneous\\xbox_icon_replay.xbx";
break;
}
default:
{
Dbg_MsgAssert( 0, ( "Bad file type sent to s_make_xbox_dir_and_icons" ));
break;
}
}
// Now load the .ico file into memory
uint32 FileSize = 0;
char* pIco = NULL;
File::StopStreaming();
// Get the file interface.
void *pFP = File::Open(pSourceIconFile, "rb");
Dbg_MsgAssert(pFP,("No %s file found.",pSourceIconFile));
FileSize = File::GetFileSize( pFP );
// Allocate a buffer to hold the file.
Mem::Manager::sHandle().PushContext( Mem::Manager::sHandle().TopDownHeap());
// Allocating the size rounded up to a multiple of 2048 in case file reading only reads a whole number of sectors.
pIco = (char*)Mem::Malloc(( FileSize + 2047 ) & ~2047 );
Dbg_MsgAssert( pIco, ( "Could not allocate memory for %s", pSourceIconFile ));
Mem::Manager::sHandle().PopContext();
// Read the file into the buffer and close the file.
#ifdef __NOPT_ASSERT__
long BytesRead=
#endif
File::Read( pIco, 1, FileSize, pFP );
Dbg_MsgAssert( (uint32)BytesRead <= FileSize, ( "Bad rwfread when loading %s", pSourceIconFile ));
File::Close( pFP );
bool SavedOK = false;
char pFullIcoName[100];
sprintf( pFullIcoName, "\\%s\\%s", p_low_level_directory_name, pIcoName );
Dbg_MsgAssert( strlen( pFullIcoName ) < 100,( "Ooops" ));
Mc::File* pFile = p_card->Open( pFullIcoName, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE );
if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
{
*p_insufficientSpace=true;
}
if( pFile )
{
uint32 CardBytesWritten = pFile->Write( pIco, FileSize );
if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
{
*p_insufficientSpace=true;
}
if( CardBytesWritten == FileSize )
{
SavedOK = true;
}
if( !pFile->Flush())
{
SavedOK = false;
}
if( !pFile->Close())
{
SavedOK = false;
}
delete pFile;
}
Mem::Free( pIco );
return SavedOK;
}
static void s_generate_card_directory_name(uint32 fileType, const char *p_name, char *p_card_directory_name)
{
Dbg_MsgAssert(p_card_directory_name,("NULL p_card_directory_name"));
char p_ascii_checksum[20];
s_generate_ascii_checksum(p_ascii_checksum,p_name,fileType);
const char *p_header=Config::GetMemCardHeader();
sprintf(p_card_directory_name,"/%s%s",p_header,p_ascii_checksum);
Dbg_MsgAssert(strlen(p_card_directory_name)<MAX_CARD_FILE_NAME_CHARS,("Oops"));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
#ifdef __PLAT_NGPS__ // Only compile on NGPS cos sceMcIconSys won't compile on other platforms.
static bool s_make_ps2_dir_and_icons( Mc::Card *p_card,
uint32 fileType, const char *p_name,
char *p_card_file_name,
bool *p_insufficientSpace)
{
char p_directory_name[MAX_CARD_FILE_NAME_CHARS+1];
s_generate_card_directory_name(fileType,p_name,p_directory_name);
Dbg_MsgAssert(p_card_file_name,("NULL p_card_file_name"));
sprintf(p_card_file_name,"%s%s",p_directory_name,p_directory_name);
Dbg_MsgAssert(strlen(p_card_file_name)<MAX_CARD_FILE_NAME_CHARS+1,("Oops"));
// Check whether the icon file exists
char p_icon_sys[MAX_CARD_FILE_NAME_CHARS+1];
sprintf(p_icon_sys,"%s/icon.sys",p_directory_name);
Dbg_MsgAssert(strlen(p_icon_sys)<MAX_CARD_FILE_NAME_CHARS+1,("Oops"));
Dbg_MsgAssert(p_card,("NULL p_card"));
Mc::File* pFile=p_card->Open( p_icon_sys, Mc::File::mMODE_READ );
if (pFile)
{
// Icon exists already, hence so must the directory, so no need to create it.
pFile->Flush();
pFile->Close();
delete pFile;
pFile=NULL;
return true;
}
// Icon does not exist hence the directory cannot either,
// so create it and save out the icon.
if (!p_card->MakeDirectory(p_directory_name))
{
if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
{
*p_insufficientSpace=true;
}
return false;
}
sceMcIconSys IconSys;
sceVu0IVECTOR bgcolor[4] =
{
{ 0x80, 0, 0, 0 },
{ 0, 0x80, 0, 0 },
{ 0, 0, 0x80, 0 },
{ 0x80, 0x80, 0x80, 0 },
};
sceVu0FVECTOR lightdir[3] =
{
{ 0.5, 0.0, 0.0, 0.0 },
{ 0.0, 0.5, 0.0, 0.0 },
{ 0.0, 0.0, 0.5, 0.0 },
};
sceVu0FVECTOR lightcol[3] =
{
{ 0.50, 0.50, 0.50, 0.00 },
{ 0.25, 0.25, 0.40, 0.00 },
{ 0.80, 0.80, 0.80, 0.00 },
};
sceVu0FVECTOR ambient = { 0.50, 0.50, 0.50, 0.00 };
memset(&IconSys, 0, sizeof(IconSys));
strcpy((char*)IconSys.Head, "PS2D");
// Convert the title from ascii to shift-jis & write it in.
//static char pTitle[100];
char pTitle[100];
char *pSourceIconFile="";
char *pIcoName="";
#ifdef __NOPT_ASSERT__
uint32 ExpectedIcoSize=0;
#endif
int LineBreak=16;
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
{
strcpy(pTitle,Script::GetString(CRCD(0x130ab28a,"cfuncs_str_thugstoryskater")));
LineBreak=strlen(pTitle);
Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
strcat(pTitle,p_name);
pIcoName="thps5.ico";
pSourceIconFile="memcard\\thps3.icn";
#ifdef __NOPT_ASSERT__
ExpectedIcoSize=Script::GetInt(CRCD(0x4925ed9e,"OptionsProsIconSpaceRequired"));
#endif
break;
}
case 0xca41692d: // NetworkSettings
{
strcpy(pTitle,Script::GetString(CRCD(0x92c497ae,"cfuncs_str_thugnetsettings")));
LineBreak=strlen(pTitle);
Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
strcat(pTitle,p_name);
// For the moment use the options/pros icon, until a new icon is made.
// TODO: Once got a new icon file, remember to update ScriptDeleteMemCardFile too.
pIcoName="network.ico";
pSourceIconFile="memcard\\network.icn";
#ifdef __NOPT_ASSERT__
ExpectedIcoSize=Script::GetInt(CRCD(0xf3431f63,"NetworkSettingsIconSpaceRequired"));
#endif
break;
}
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
/*
#if ENGLISH
sprintf(pTitle,"THUG Skater:%s",p_name);
#else
sprintf(pTitle,Script::GetLocalString(CRCD(0x7c2cfe2c,"cfuncs_str_thps3skater")),p_name);
#endif
LineBreak=12; // After 'THUG Skater:'
pIcoName="cas.ico";
pSourceIconFile="memcard\\cas.icn";
#ifdef __NOPT_ASSERT__
ExpectedIcoSize=Script::GetInt(CRCD(0xa79ee524,"CASIconSpaceRequired"));
#endif
*/
break;
}
case 0x61a1bc57: // Cat
{
strcpy(pTitle,Script::GetString(CRCD(0x3aa2cffa,"cfuncs_str_thugtrick")));
LineBreak=strlen(pTitle);
Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
strcat(pTitle,p_name);
pIcoName="tricks.ico";
pSourceIconFile="memcard\\tricks.icn";
#ifdef __NOPT_ASSERT__
ExpectedIcoSize=Script::GetInt(CRCD(0x46b0ef40,"CATIconSpaceRequired"));
#endif
break;
}
case 0x62896edf: // CreatedGoals
{
strcpy(pTitle,Script::GetString(CRCD(0x257678cb,"cfuncs_str_thuggoals")));
LineBreak=strlen(pTitle);
Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
strcat(pTitle,p_name);
pIcoName="goals.ico";
pSourceIconFile="memcard\\goals.icn";
#ifdef __NOPT_ASSERT__
ExpectedIcoSize=Script::GetInt(CRCD(0x3205bc07,"CreatedGoalsIconSpaceRequired"));
#endif
break;
}
case 0x3bf882cc: // Park
{
strcpy(pTitle,Script::GetString(CRCD(0x2171fddc,"cfuncs_str_thugpark")));
LineBreak=strlen(pTitle);
Dbg_MsgAssert(LineBreak<=16,("Mem card title '%s' exceeds 16 chars",pTitle));
Dbg_MsgAssert(pTitle[LineBreak-1]==':',("Expected mem card file name '%s' to end in ':'",pTitle));
strcat(pTitle,p_name);
pIcoName="parked.ico";
pSourceIconFile="memcard\\parked.icn";
#ifdef __NOPT_ASSERT__
ExpectedIcoSize=Script::GetInt(CRCD(0x82aaf8dc,"ParkIconSpaceRequired"));
#endif
break;
}
default:
{
Dbg_MsgAssert(0,("Bad file type sent to s_create_mem_card_icon_file"));
break;
}
}
// Convert the title to shift-jis and put it in IconSys.TitleName
int Len=strlen(pTitle);
Dbg_MsgAssert(Len<=32,("Title too long !!!"));
pTitle[32]=0; // Just to be absolutely sure on non debug builds.
char *pTit=(char*)IconSys.TitleName;
for (int i=0; i<Len; ++i)
{
unsigned short Jis=s_ascii_to_sjis(pTitle[i]);
*pTit++=(Jis>>8)&0xff;
*pTit++=Jis&0xff;
}
*pTit++=0;
*pTit=0;
IconSys.OffsLF = 2*LineBreak;
IconSys.TransRate = 0x60;
memcpy(IconSys.BgColor, bgcolor, sizeof(bgcolor));
memcpy(IconSys.LightDir, lightdir, sizeof(lightdir));
memcpy(IconSys.LightColor, lightcol, sizeof(lightcol));
memcpy(IconSys.Ambient, ambient, sizeof(ambient));
strcpy((char*)IconSys.FnameView, pIcoName);
strcpy((char*)IconSys.FnameCopy, pIcoName);
strcpy((char*)IconSys.FnameDel, pIcoName);
// Now load the .ico file into memory
uint32 FileSize=0;
char *pIco=NULL;
File::StopStreaming();
void *pFP = File::Open(pSourceIconFile, "rb");
Dbg_MsgAssert(pFP,("No %s file found.",pSourceIconFile));
FileSize=File::GetFileSize(pFP);
// Make sure the ico file size matches that specified in memcard.q.
// The +1 is because the icon.sys takes up 1K.
Dbg_MsgAssert(1+(((FileSize+0x3ff)&(~0x3ff)) >> 10)==ExpectedIcoSize,("%s icon size mismatch",Script::FindChecksumName(fileType)));
// Allocate a buffer to hold the file.
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
// Allocating the size rounded up to a multiple of 2048 in case file reading only
// reads a whole number of sectors.
pIco=(char*)Mem::Malloc((FileSize+2047)&~2047);
Dbg_MsgAssert(pIco,("Could not allocate memory for %s",pSourceIconFile));
Mem::Manager::sHandle().PopContext();
// Read the file into the buffer and close the file.
#ifdef __NOPT_ASSERT__
long BytesRead=
#endif
File::Read(pIco,1,FileSize,pFP);
Dbg_MsgAssert(BytesRead<=FileSize,("Bad rwfread when loading %s",pSourceIconFile));
File::Close(pFP);
bool SavedOK=false;
pFile=p_card->Open( p_icon_sys, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE );
if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
{
*p_insufficientSpace=true;
}
if (pFile)
{
//printf("Writing %s, %d bytes\n",pIconSys,sizeof(IconSys));
uint32 CardBytesWritten=pFile->Write( &IconSys, sizeof(IconSys) );
if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
{
*p_insufficientSpace=true;
}
if (CardBytesWritten==sizeof(IconSys))
{
SavedOK=true;
}
if (!pFile->Flush())
{
SavedOK=false;
}
if (!pFile->Close())
{
SavedOK=false;
}
delete pFile;
}
// Bail out straight away if the previous save failed so that the error code as
// returned by Card::GetLastError is correct.
if (!SavedOK)
{
Mem::Free(pIco);
return false;
}
SavedOK=false;
char pFullIcoName[MAX_CARD_FILE_NAME_CHARS+1];
sprintf(pFullIcoName,"%s/%s",p_directory_name,pIcoName);
Dbg_MsgAssert(strlen(pFullIcoName)<MAX_CARD_FILE_NAME_CHARS,("Ooops"));
pFile=p_card->Open( pFullIcoName, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE );
if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
{
*p_insufficientSpace=true;
}
if (pFile)
{
//printf("Writing %s, %d bytes\n",pFullIcoName,FileSize);
uint32 CardBytesWritten=pFile->Write( pIco, FileSize );
if (p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE)
{
*p_insufficientSpace=true;
}
if (CardBytesWritten==FileSize)
{
SavedOK=true;
}
if (!pFile->Flush())
{
SavedOK=false;
}
if (!pFile->Close())
{
SavedOK=false;
}
delete pFile;
}
Mem::Free(pIco);
return SavedOK;
}
#else
static bool s_make_ps2_dir_and_icons( Mc::Card *p_card,
uint32 fileType, const char *p_name,
char *p_card_file_name,
bool *p_insufficientSpace)
{
return false;
}
#endif // #ifdef __PLAT_NGPS__
#ifdef __PLAT_NGC__
static bool s_insert_ngc_icon( SMcFileHeader *p_fileHeader,
Mc::Card *p_card,
Mc::File *p_file,
uint32 fileType,
const char *p_name)
{
Dbg_MsgAssert(p_fileHeader,("NULL p_fileHeader"));
Dbg_MsgAssert(p_card,("NULL p_card"));
Dbg_MsgAssert(p_file,("NULL p_file"));
Dbg_MsgAssert(p_name,("NULL p_name"));
uint8 *p_buffer=p_fileHeader->mpIconData;
char* p_icon_name="icons\\CreateSk8r_Icon_01.img.ngc";
switch( fileType )
{
case 0xb010f357: // OptionsAndPros
case 0xca41692d: // NetworkSettings
{
break;
}
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
break;
}
case 0x61a1bc57: // CAT
{
p_icon_name = "icons\\Trick_Icon_01.img.ngc";
break;
}
case 0x62896edf: // CreatedGoals
{
p_icon_name = "icons\\Goal_Icon_01.img.ngc";
break;
}
case 0x3bf882cc: // Park
{
p_icon_name = "icons\\ParkEd_Icon_01.img.ngc";
break;
}
default:
{
Dbg_MsgAssert( 0, ( "Bad file type" ));
break;
}
}
// We used to have each of the save types in the switch statement above choose a different
// name, one which contained the save type, ie "THUG: Skater"
// We then had to change them to all have the same name, namely "Tony Hawk's Underground"
// This is because Nintendo required that we prefix all our save names with Tony Hawk's Underground, but
// when we tried to append the save type as well the text was too long for the IPL screen.
strcpy((char*)p_buffer, Script::GetString(CRCD(0x6a6214bc,"cfuncs_str_ngc_generic_save_name")));
Dbg_MsgAssert(strlen((const char*)p_buffer) < 32,("p_buffer '%s' too long",p_buffer));
strcpy((char*)p_buffer + 32, p_name );
// Find the index of the number character that precedes the .
// This used to be set a hard wired value for each of the cases above, but changed to
// calculate it instead cos the icon file names could quite likely change.
int name_index=0;
while (p_icon_name[name_index] && p_icon_name[name_index] != '.')
{
++name_index;
}
Dbg_MsgAssert(p_icon_name[name_index],("No '.' found in icon name '%s'",p_icon_name));
--name_index;
CARDStat stat;
CARDGetStatus( p_card->GetSlot(), p_file->m_file_number, &stat );
// Set up to write the banner and icon data.
char* next_pixel = (char*)p_buffer + 64;
// Load the banner file.
void *p_FH = File::Open( "icons\\thps3_bannericon_01.img.ngc", "rb" );
Dbg_MsgAssert( p_FH, ( "Couldn't open texture file %s\n", "icons\\thps3_bannericon_01.img.ngc" ) );
int dummy;
int width;
int height;
// Read header info.
File::Read( &dummy, sizeof( int ), 1, p_FH ); // version
File::Read( &dummy, sizeof( int ), 1, p_FH ); // checksum
File::Read( &width, sizeof( int ), 1, p_FH );
File::Read( &height, sizeof( int ), 1, p_FH );
File::Read( &dummy, sizeof( int ), 1, p_FH ); // depth
File::Read( &dummy, sizeof( int ), 1, p_FH ); // levels
File::Read( &dummy, sizeof( int ), 1, p_FH ); // rwidth
File::Read( &dummy, sizeof( int ), 1, p_FH ); // rheight
File::Read( &dummy, sizeof( int ), 1, p_FH ); // has_holes/alphamap
Dbg_MsgAssert( width == 96, ( "Bad mem card banner width" ));
Dbg_MsgAssert( height == 32, ( "Bad mem card banner height" ));
// Read image & palette data.
File::Read( next_pixel, 96 * 32, 1, p_FH );
next_pixel += ( 96 * 32 );
File::Read( next_pixel, 256 * 2, 1, p_FH );
next_pixel += ( 256 * 2 );
File::Close( p_FH );
# if 0
// Write out a dummy array representing the banner image.
OSReport( "unsigned short image[96*32] = {\n" );
for( int i = 0; i < 32; ++i )
{
for( int j = 0; j < 96; ++j )
{
OSReport( "0x%x,", ((unsigned short*)p_texture->m_pPalette )[((unsigned char*)p_texture->m_pImage )[( i * 32 ) + j]] );
}
OSReport( "\n" );
}
OSReport( "};\n" );
# endif
// Load the icon files.
for( int i = 0; i < 7; ++i )
{
p_icon_name[name_index] = '1' + i;
// Load the icon file.
void *p_FH = File::Open( p_icon_name, "rb" );
Dbg_MsgAssert( p_FH, ( "Couldn't open icon file %s\n", p_icon_name ) );
int dummy;
int width;
int height;
// Read header info.
File::Read( &dummy, sizeof( int ), 1, p_FH ); // version
File::Read( &dummy, sizeof( int ), 1, p_FH ); // checksum
File::Read( &width, sizeof( int ), 1, p_FH );
File::Read( &height, sizeof( int ), 1, p_FH );
File::Read( &dummy, sizeof( int ), 1, p_FH ); // depth
File::Read( &dummy, sizeof( int ), 1, p_FH ); // levels
File::Read( &dummy, sizeof( int ), 1, p_FH ); // rwidth
File::Read( &dummy, sizeof( int ), 1, p_FH ); // rheight
File::Read( &dummy, sizeof( int ), 1, p_FH ); // has_holes/alphamap
Dbg_MsgAssert( width == 32, ( "Bad mem card banner width" ));
Dbg_MsgAssert( height == 32, ( "Bad mem card banner height" ));
// Read image & palette data.
File::Read( next_pixel, 32 * 32, 1, p_FH );
next_pixel += ( 32 * 32 );
if( i == 6 )
{
File::Read( next_pixel, 256 * 2, 1, p_FH );
next_pixel += 256*2;
}
File::Close( p_FH );
// Icon formats.
CARDSetIconFormat( &stat, i, CARD_STAT_ICON_C8 );
// Anim speeds.
CARDSetIconSpeed( &stat, i, CARD_STAT_SPEED_MIDDLE );
}
Dbg_MsgAssert(next_pixel==(char*)p_buffer+NGC_MEMCARD_ICON_DATA_SIZE,("Incorrect NGC_MEMCARD_ICON_DATA_SIZE, too big by %d bytes",NGC_MEMCARD_ICON_DATA_SIZE-(next_pixel-(char*)p_buffer)));
stat.commentAddr = (uint32)p_fileHeader->mpIconData-(uint32)p_fileHeader;
stat.iconAddr = stat.commentAddr + 64; // End of comment strings.
// Banner format.
CARDSetBannerFormat( &stat, CARD_STAT_BANNER_C8 );
CARDSetIconSpeed( &stat, 7, CARD_STAT_SPEED_END );
CARDSetIconAnim( &stat, CARD_STAT_ANIM_LOOP );
CARDSetStatus( p_card->GetSlot(), p_file->m_file_number, &stat );
return true;
}
#else
static bool s_insert_ngc_icon( SMcFileHeader *p_fileHeader,
Mc::Card *p_card,
Mc::File *p_file,
uint32 fileType,
const char *p_name)
{
return false;
}
#endif // #ifdef __PLAT_NGC__
static bool s_first_date_is_more_recent(Script::CStruct *p_a, Script::CStruct *p_b)
{
int year_a=0;
int month_a=0;
int day_a=0;
int hour_a=0;
int minutes_a=0;
int seconds_a=0;
Dbg_MsgAssert(p_a,("NULL p_a"));
p_a->GetInteger(CRCD(0x447d8cc8,"Year"),&year_a);
p_a->GetInteger(CRCD(0x7149eff9,"Month"),&month_a);
p_a->GetInteger(CRCD(0x1a5fd66f,"Day"),&day_a);
p_a->GetInteger(CRCD(0x8fe1eeb1,"Hour"),&hour_a);
p_a->GetInteger(CRCD(0x5f94b55c,"Minutes"),&minutes_a);
p_a->GetInteger(CRCD(0xd029f619,"Seconds"),&seconds_a);
int year_b=0;
int month_b=0;
int day_b=0;
int hour_b=0;
int minutes_b=0;
int seconds_b=0;
Dbg_MsgAssert(p_b,("NULL p_b"));
p_b->GetInteger(CRCD(0x447d8cc8,"Year"),&year_b);
p_b->GetInteger(CRCD(0x7149eff9,"Month"),&month_b);
p_b->GetInteger(CRCD(0x1a5fd66f,"Day"),&day_b);
p_b->GetInteger(CRCD(0x8fe1eeb1,"Hour"),&hour_b);
p_b->GetInteger(CRCD(0x5f94b55c,"Minutes"),&minutes_b);
p_b->GetInteger(CRCD(0xd029f619,"Seconds"),&seconds_b);
bool more_recent=false;
if (year_a < year_b)
{
}
else if (year_a > year_b)
{
more_recent=true;
}
else if (month_a < month_b)
{
}
else if (month_a > month_b)
{
more_recent=true;
}
else if (day_a < day_b)
{
}
else if (day_a > day_b)
{
more_recent=true;
}
else if (hour_a < hour_b)
{
}
else if (hour_a > hour_b)
{
more_recent=true;
}
else if (minutes_a < minutes_b)
{
}
else if (minutes_a > minutes_b)
{
more_recent=true;
}
else if (seconds_a < seconds_b)
{
}
else if (seconds_a > seconds_b)
{
more_recent=true;
}
else
{
// Exactly the same date/time, so call it more recent.
more_recent=true;
}
return more_recent;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | GetMostRecentSave | Finds the most recent save of the given type in the passed
// directory listing. Puts the result into a structure called MostRecentSave. Any existing
// parameter called MostRecentSave will be removed first. If there are no files, no MostRecentSave
// parameter will be created.
// @uparmopt [] | The directory listing. Must be an array of structures.
// @uparmopt name | The save type. If omitted it will return the most recent save of any type.
bool ScriptGetMostRecentSave(Script::CStruct *pParams, Script::CScript *pScript)
{
pScript->GetParams()->RemoveComponent("MostRecentSave");
CArray *p_array=NULL;
pParams->GetArray(NONAME,&p_array);
if (!p_array)
{
return false;
}
uint32 file_type=0;
pParams->GetChecksum(NONAME,&file_type);
CStruct *p_most_recent=NULL;
for (uint32 i=0; i<p_array->GetSize(); ++i)
{
CStruct *p_struct=p_array->GetStructure(i);
uint32 this_file_type=0;
p_struct->GetChecksum("file_type",&this_file_type);
if (file_type && file_type!=this_file_type)
{
continue;
}
if (p_struct->ContainsFlag("BadVersion") ||
p_struct->ContainsFlag("Corrupt"))
{
if (Config::GetHardware() == Config::HARDWARE_XBOX ||
Config::GetHardware() == Config::HARDWARE_NGC)
{
// For the XBox, do not ignore bad files, so that an error message will appear on bootup
// if the most recent save is bad. (TT6236)
// Same for NGC (TT13519)
}
else
{
// For other platforms, find the most recent good save.
continue;
}
}
if (p_most_recent)
{
if (s_first_date_is_more_recent(p_struct, p_most_recent))
{
p_most_recent=p_struct;
}
}
else
{
p_most_recent=p_struct;
}
}
if (p_most_recent)
{
pScript->GetParams()->AddStructure("MostRecentSave",p_most_recent);
return true;
}
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | GetMemCardSpaceAvailable | Puts the amount of space left on the mem card
// into the parameter SpaceAvailable. Units are K for the PS2
bool ScriptGetMemCardSpaceAvailable(Script::CStruct *pParams, Script::CScript *pScript)
{
pScript->GetParams()->AddInteger(CRCD(0xc37c363,"SpaceAvailable"),0);
pScript->GetParams()->AddInteger(CRCD(0x855b2fc,"FilesLeft"),1000000);
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (p_card)
{
pScript->GetParams()->AddInteger(CRCD(0xc37c363,"SpaceAvailable"),p_card->GetNumFreeClusters());
#ifdef __PLAT_NGC__
pScript->GetParams()->AddInteger(CRCD(0x855b2fc,"FilesLeft"),p_card->CountFilesLeft());
#endif
return true;
}
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | GetMemCardSpaceRequired | Calculates the amount of space required to save the specified
// file type to the memory card, and puts it into the parameter SpaceRequired.
// Units are K for the PS2.
// @uparm name | The save type.
bool ScriptGetMemCardSpaceRequired(Script::CStruct *pParams, Script::CScript *pScript)
{
uint32 file_type=0;
pParams->GetChecksum(NONAME,&file_type);
// s_calculate_total_space_used_on_card adds in the space used by the icons.
int space_required=s_calculate_total_space_used_on_card(file_type,sGetFixedFileSize(file_type));
pScript->GetParams()->AddInteger("SpaceRequired",space_required);
return true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | MemCardFileExists | returns true if the file already exists
// on the card
// @parm string | Name | the name of the file
// @parm name | type | the type of the file
bool ScriptMemCardFileExists(Script::CStruct *pParams, Script::CScript *pScript)
{
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (!p_card)
{
return false;
}
const char *p_name=NULL;
pParams->GetText("Name",&p_name);
uint32 file_type=0;
pParams->GetChecksum("Type",&file_type);
if (!p_name || !file_type)
{
return false;
}
char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
p_card_file_name[0]=0;
const char *p_xbox_directory_name=NULL;
const char *p_xbox_converted_directory_name=NULL;
switch (Config::GetHardware())
{
case Config::HARDWARE_XBOX:
{
p_xbox_directory_name=s_generate_xbox_directory_name(file_type,p_name);
p_xbox_converted_directory_name=p_card->ConvertDirectory(p_xbox_directory_name);
if (!p_xbox_converted_directory_name)
{
return false;
}
sprintf(p_card_file_name,"/%s/%s",p_xbox_converted_directory_name,p_xbox_converted_directory_name);
Dbg_MsgAssert(strlen(p_card_file_name)<MAX_CARD_FILE_NAME_CHARS,("Oops"));
break;
}
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
case Config::HARDWARE_NGC:
{
const char *p_header=Config::GetMemCardHeader();
char p_ascii_checksum[20];
s_generate_ascii_checksum(p_ascii_checksum,p_name,file_type);
sprintf(p_card_file_name,"/%s%s/%s%s", p_header,p_ascii_checksum,
p_header,p_ascii_checksum);
Dbg_MsgAssert(strlen(p_card_file_name)<MAX_CARD_FILE_NAME_CHARS,("Oops"));
break;
}
default:
break;
}
Mc::File* pFile = p_card->Open( p_card_file_name, Mc::File::mMODE_READ );
if (pFile)
{
int file_size=pFile->Seek(0,Mc::File::BASE_END);
int total_size_on_card=s_calculate_total_space_used_on_card(file_type,file_size);
pScript->GetParams()->AddInteger("total_file_size",total_size_on_card);
pFile->Flush();
pFile->Close();
delete pFile;
return true;
}
if (Config::GetHardware()==Config::HARDWARE_XBOX)
{
// Better delete the directory we just created ...
Dbg_MsgAssert(p_xbox_directory_name,("NULL p_xbox_directory_name ?"));
p_card->DeleteDirectory(p_xbox_directory_name);
}
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | DeleteMemCardFile | Deletes the specified file from the memory card
// @parmopt string | CardFileName | | The name of the file as stored on the memory card. If this is
// not specified then it is derived from UserFileName and Type
// @parmopt string | UserFileName | | The name of the file as it appears in the files menu
// @parm name | Type | The file type (NetworkSettings, OptionsAndPros, Cas, Park, Replay)
bool ScriptDeleteMemCardFile(Script::CStruct *pParams, Script::CScript *pScript)
{
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic(true);
Pcm::PauseStream(true);
}
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (!p_card)
{
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic( -1 );
Pcm::PauseStream( -1 );
}
return false;
}
bool delete_succeeded=true;
const char *p_xbox_directory_name=NULL;
pParams->GetString("XBoxDirectoryName",&p_xbox_directory_name);
const char *p_full_file_name=NULL;
pParams->GetString("CardFileName",&p_full_file_name);
uint32 file_type=0;
pParams->GetChecksum("Type",&file_type);
const char *p_name="";
pParams->GetString("UserFileName",&p_name);
switch (Config::GetHardware())
{
case Config::HARDWARE_NGC:
{
if (p_full_file_name)
{
if (!p_card->Delete(p_full_file_name))
{
delete_succeeded=false;
}
}
else
{
char p_temp[MAX_CARD_FILE_NAME_CHARS+1];
// If p_full_file_name is not specified then derive it from Type and UserFileName
const char *p_header=Config::GetMemCardHeader();
char p_ascii_checksum[20];
s_generate_ascii_checksum(p_ascii_checksum,p_name,file_type);
sprintf(p_temp,"%s%s%s%s",p_header,p_ascii_checksum,
p_header,p_ascii_checksum);
Dbg_MsgAssert(strlen(p_temp)<MAX_CARD_FILE_NAME_CHARS,("Oops"));
if (!p_card->Delete(p_temp))
{
delete_succeeded=false;
}
}
break;
}
case Config::HARDWARE_XBOX:
{
if (!p_xbox_directory_name)
{
// Derive p_xbox_directory_name from the file type and user file name
p_xbox_directory_name=s_generate_xbox_directory_name(file_type,p_name);
}
if (!p_card->DeleteDirectory( p_xbox_directory_name ))
{
delete_succeeded=false;
}
break;
}
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
{
char p_temp[MAX_CARD_FILE_NAME_CHARS+1];
if (!p_full_file_name)
{
// If p_full_file_name is not specified then derive it from Type and UserFileName
const char *p_header=Config::GetMemCardHeader();
char p_ascii_checksum[20];
s_generate_ascii_checksum(p_ascii_checksum,p_name,file_type);
sprintf(p_temp,"/%s%s/%s%s",p_header,p_ascii_checksum,
p_header,p_ascii_checksum);
Dbg_MsgAssert(strlen(p_temp)<MAX_CARD_FILE_NAME_CHARS,("Oops"));
p_full_file_name=p_temp;
}
int len=strlen(p_full_file_name);
uint32 file_type=s_determine_file_type(p_full_file_name[len-1]);
// Generate the directory name.
Dbg_MsgAssert(len<100,("File name too long"));
char p_directory_name[100];
char *p_dest=p_directory_name;
const char *p_source=p_full_file_name;
// Copy the first '/'
*p_dest++=*p_source++;
while (*p_source && *p_source!='/')
{
*p_dest++=*p_source++;
}
*p_dest=0;
// Delete the file that has the same name as the directory
if (!p_card->Delete(p_full_file_name))
{
delete_succeeded=false;
}
// Delete the icon.sys file
char p_buf[100];
sprintf(p_buf,"%s/icon.sys",p_directory_name);
Dbg_MsgAssert(strlen(p_buf)<100,("Oops"));
if (!p_card->Delete(p_buf))
{
delete_succeeded=false;
}
// Delete the .ico file.
switch (file_type)
{
case 0xffc529f4: // Cas
{
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
//sprintf(p_buf,"%s/cas.ico",p_directory_name);
break;
}
case 0x61a1bc57: // CAT
{
sprintf(p_buf,"%s/tricks.ico",p_directory_name);
break;
}
case 0x3bf882cc: // Park
{
sprintf(p_buf,"%s/parked.ico",p_directory_name);
break;
}
case 0x26c80b0d: // Replay
{
sprintf(p_buf,"%s/replay.ico",p_directory_name);
break;
}
case 0xb010f357: // OptionsAndPros
{
sprintf(p_buf,"%s/thps5.ico",p_directory_name);
break;
}
case 0x62896edf: // CreatedGoals
{
sprintf(p_buf,"%s/goals.ico",p_directory_name);
break;
}
case 0xca41692d: // NetworkSettings
{
// TODO: Change this once got a new icon file for the net settings
sprintf(p_buf,"%s/network.ico",p_directory_name);
break;
}
default:
{
Dbg_MsgAssert(0,("Bad file type sent to DeleteMemCardFile"));
break;
}
}
Dbg_MsgAssert(strlen(p_buf)<100,("Oops"));
if (!p_card->Delete(p_buf))
{
delete_succeeded=false;
}
// Delete the directory
if (!p_card->DeleteDirectory(p_directory_name))
{
delete_succeeded=false;
}
break;
}
default:
{
delete_succeeded=false;
break;
}
}
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic( -1 );
Pcm::PauseStream( -1 );
}
return delete_succeeded;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | FormatCard | Formats the memory card
bool ScriptFormatCard(Script::CStruct *pParams, Script::CScript *pScript)
{
Pcm::PauseMusic(true);
Pcm::PauseStream(true);
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
bool Worked=false;
if (p_card)
{
Worked=p_card->Format();
}
Pcm::PauseMusic( -1 );
Pcm::PauseStream( -1 );
return Worked;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | CardIsInSlot | returns true if the memory card is in the slot
bool ScriptCardIsInSlot(Script::CStruct *pParams, Script::CScript *pScript)
{
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (p_card)
{
return true;
}
else
{
#ifdef __PLAT_NGC__
// On the NGC, p_card will also be NULL if the card is in the slot, but is broken.
if (mc_man->GotFatalError())
{
return true;
}
// On the NGC, p_card will also be NULL if something is in the slot,
// but gives a 'wrong device' error
if (mc_man->GotWrongDevice())
{
return true;
}
#endif
return false;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | CardIsFormatted | returns true if the memory card is formatted
bool ScriptCardIsFormatted(Script::CStruct *pParams, Script::CScript *pScript)
{
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (p_card)
{
return p_card->IsFormatted();
}
else
{
// If there is no card, then I guess it isn't formatted.
return false;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | SaveFailedDueToInsufficientSpace |
bool ScriptSaveFailedDueToInsufficientSpace(Script::CStruct *pParams, Script::CScript *pScript)
{
return s_insufficient_space;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | GetSummaryInfo |
bool ScriptGetSummaryInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
uint32 file_type=0;
pParams->GetChecksum(NONAME,&file_type);
int use_vault_data=0;
pParams->GetInteger(CRCD(0xc78dabf6,"VaultData"),&use_vault_data);
if (use_vault_data)
{
file_type=sVaultDataType;
Dbg_MsgAssert(spVaultData,("Called GetSummaryInfo on the vault data when there was no vault data"));
s_generate_summary_info(pScript->GetParams(), file_type, spVaultData);
}
else
{
if (file_type==0xb010f357) // OptionsAndPros
{
// Force the goal manager to refresh the goal manager params structure.
// Note that this cannot be done inside s_generate_summary_info, because LevelUnload()
// will create a structure and store a pointer to it.
// In other places SSwitchToNextPool() will be called before calling s_generate_summary_info,
// and we don't want the structure created by LevelUnload() to come off the special memcard pool.
Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
p_goal_manager->LevelUnload();
}
s_generate_summary_info(pScript->GetParams(), file_type, NULL);
}
return true;
}
#if __USE_REPLAYS__
static int sGetReplayDataSize(int num_dummies)
{
return sizeof(Replay::SReplayDataHeader)+
num_dummies*sizeof(Replay::SSavedDummy)+
Replay::GetBufferSize()+
4; // The checksum of that lot
}
#endif
static uint32 sGetFixedFileSize(uint32 fileType)
{
uint32 fixed_size=0;
switch (fileType)
{
case 0xb010f357: // OptionsAndPros
fixed_size=Script::GetInteger(CRCD(0xe7f51ffe,"SaveSize_OptionsAndPros"),Script::ASSERT);
break;
case 0xffc529f4: // Cas
Dbg_MsgAssert(0,("CAS mem card files are no longer supported! Use OptionsAndPros file type instead"));
//fixed_size=Script::GetInteger("SaveSize_Cas",Script::ASSERT);
break;
case 0x61a1bc57: // Cat
fixed_size=Script::GetInteger(CRCD(0x9eecdec9,"SaveSize_Cat"),Script::ASSERT);
break;
case 0x62896edf: // CreatedGoals
fixed_size=Script::GetInteger(CRCD(0x3308efc0,"SaveSize_CreatedGoals"),Script::ASSERT);
break;
case 0x3bf882cc: // Park
fixed_size=Script::GetInteger(CRCD(0x2cb071ed,"SaveSize_Park"),Script::ASSERT);
break;
case 0x26c80b0d: // Replay
fixed_size=Script::GetInteger(CRCD(0x8ee3aa00,"SaveSize_Replay"),Script::ASSERT);
break;
case 0xca41692d: // NetworkSettings
fixed_size=Script::GetInteger(CRCD(0x651c978d,"SaveSize_NetworkSettings"),Script::ASSERT);
break;
default:
Dbg_MsgAssert(0,("Bad file_type of %s",Script::FindChecksumName(fileType)));
break;
}
fixed_size+=sizeof(SMcFileHeader);
fixed_size=s_round_up_to_platforms_block_size(fixed_size);
return fixed_size;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// @script | SaveToMemoryCard |
bool ScriptSaveToMemoryCard(Script::CStruct *pParams, Script::CScript *pScript)
{
if (Config::GetHardware() == Config::HARDWARE_NGC)
{
// On the GameCube, the temp mem card pools only exist for the duration of this function,
// because they use space normally used for rendering, hence cannot exist during the mem card scripts
// which may execute over many frames.
ScriptCreateTemporaryMemCardPools(NULL,NULL);
}
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic(true);
Pcm::PauseStream(true);
}
s_insufficient_space=false;
const char *p_name="";
pParams->GetText("Name",&p_name);
uint32 file_type=0;
pParams->GetChecksum("Type",&file_type);
// If the SaveVaultData flag is set, it will save the stored spVaultData, which
// will have been loaded from the online vault.
int save_vault_data=0;
pParams->GetInteger(CRCD(0xbb609e73,"SaveVaultData"),&save_vault_data);
if (save_vault_data)
{
Dbg_MsgAssert(spVaultData,("Called SaveToMemoryCard with no vault data"));
file_type=sVaultDataType;
}
if (!save_vault_data && file_type==0xb010f357) // OptionsAndPros
{
// Force the goal manager to refresh the goal manager params structure.
// This has to be done before calling SSwitchToNextPool(), because LevelUnload()
// will create a structure and store a pointer to it.
// It is best for the special pools to be kept empty outside of GetMemCardSpaceRequired
// or SaveToMemoryCard, otherwise saving the game could run out of pool space.
Game::CGoalManager* p_goal_manager=Game::GetGoalManager();
Dbg_MsgAssert(p_goal_manager,("NULL p_goal_manager"));
p_goal_manager->LevelUnload();
}
// If the special pools exist, switch to them so that the components & structs etc get allocated off them.
// The special pools use the space freed by unloading the skater anims, and are for preventing memory
// overflows when the save size gets large.
Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0,("Bad current CComponent pool"));
Dbg_MsgAssert(CStruct::SGetCurrentPoolIndex()==0,("Bad current CStruct pool"));
Dbg_MsgAssert(CVector::SGetCurrentPoolIndex()==0,("Bad current CVector pool"));
bool got_special_pools=false;
CComponent::SSwitchToNextPool();
if (CComponent::SPoolExists())
{
CStruct::SSwitchToNextPool();
Dbg_MsgAssert(CStruct::SPoolExists(),("No special CStruct pool ?"));
CVector::SSwitchToNextPool();
Dbg_MsgAssert(CVector::SPoolExists(),("No special CVector pool ?"));
got_special_pools=true;
}
else
{
CComponent::SSwitchToPreviousPool();
}
#ifdef __NOPT_ASSERT__
int initial_ccomponent_num_used_items=CComponent::SGetNumUsedItems();
int initial_cstruct_num_used_items=CStruct::SGetNumUsedItems();
int initial_cvector_num_used_items=CVector::SGetNumUsedItems();
#endif
// WARNING ! WARNING ! WARNING ! WARNING ! WARNING !
// Between here and SSwitchToPreviousPool(), all CStruct's and CComponent's will be allocated
// off a special pool.
// This is to avoid overflowing the regular pools with these temporary big structures.
// Make sure that no function, or function that it calls, stores any pointers to new CStructs
// between here and SSwitchToPreviousPool().
// This is just to keep the special pool clean. Things will get confusing if other stuff
// gets allocated off it and uses up space there, cos then saving to mem card could start
// running out of pool again.
Script::CStruct *pMemCardStuff=new Script::CStruct;
if (save_vault_data)
{
pMemCardStuff->AppendStructure(spVaultData);
}
else
{
s_insert_game_save_info(file_type,pMemCardStuff);
}
//Script::PrintContents(pMemCardStuff);
Script::CStruct *pSummaryInfo=new Script::CStruct;
s_generate_summary_info(pSummaryInfo,file_type,pMemCardStuff);
// Put the user filename into the summary info too.
pSummaryInfo->AddString("filename",p_name);
#ifdef __NOPT_ASSERT__
printf("Save type = '%s'\n",Script::FindChecksumName(file_type));
printf("Num CComponents used by save = %d\n",CComponent::SGetNumUsedItems()-initial_ccomponent_num_used_items);
printf("Num CStructs used by game save = %d\n",CStruct::SGetNumUsedItems()-initial_cstruct_num_used_items);
printf("Num CVectors used by game save = %d\n",CVector::SGetNumUsedItems()-initial_cvector_num_used_items);
#endif
Mc::File* pFile=NULL;
uint8 *pTemp=NULL;
uint8 *p_pad=NULL;
uint32 pad_size=0;
bool SavedOK=false;
uint32 CardBytesWritten=0;
uint8 *p_dest=NULL;
SMcFileHeader *p_file_header=NULL;
uint32 BytesWritten=0;
Mc::Manager * mc_man=NULL;
Mc::Card* p_card=NULL;
// fixed_size is the hard-wired file size as defined in memcard.q, rounded up to
// the platform's block size. A file of exactly this size will be opened for writing.
// The code will assert if the required space is greater than this.
// The file sizes are required to be fixed as a TRC thing for GameCube.
uint32 fixed_size=sGetFixedFileSize(file_type);
// header_and_structures_size is the exact space used by the header, summary info, and
// game save info structures. It is not rounded up to the platform's block size.
// For all save types except replays, this comprises all the info in the file.
// Replay's have the replay data tagged on the end of the file instead, because there
// is not enough memory to keep it in the structures.
uint32 header_and_structures_size=0;
// This is the size of the replay data, if any. Not rounded up to the platform's block size.
uint32 replay_data_size=0;
#if __USE_REPLAYS__
// The big replay data has to be tagged on the end rather than being put inside pMemCardStuff
// since there is not enough memory to make a copy of the replay data there.
Replay::SReplayDataHeader replay_data_header;
if (file_type==0x26c80b0d/* Replay */)
{
Replay::WriteReplayDataHeader(&replay_data_header);
replay_data_size=sGetReplayDataSize(replay_data_header.mNumStartStateDummies);
}
#endif
/////////////////////////////////////////////////////////////////////////
//pMemCardStuff->PrintContents();
//printf("Space required = %d\n",pMemCardStuff->CalculateBufferSize());
/////////////////////////////////////////////////////////////////////////
// Get how much space is needed to store the structures.
// Calculating this before opening the file so that if CalculateBufferSize fails,
// the file won't be left with zero size, erasing the previous save game.
// Passing fixed_size as the size of the temp buffer used when calculating the actual size needed.
uint32 structure_size=CalculateBufferSize(pMemCardStuff, fixed_size);
uint32 summary_info_size=CalculateBufferSize(pSummaryInfo,MAX_SUMMARY_INFO_SIZE);
Dbg_MsgAssert(summary_info_size<=MAX_SUMMARY_INFO_SIZE,("summary_info_size too big !!"));
header_and_structures_size=sizeof(SMcFileHeader)+summary_info_size+structure_size;
uint32 required_size=header_and_structures_size+replay_data_size;
#ifdef __NOPT_ASSERT__
Dbg_MsgAssert(required_size <= fixed_size,("Need to update fixed file size for type '%s'\nMin required size = %d",Script::FindChecksumName(file_type),required_size));
printf("required_size=%d, fixed_size=%d, %d percent\n",required_size,fixed_size,(100*required_size)/fixed_size);
if (required_size > fixed_size)
{
goto ERROR;
}
#endif
pad_size=fixed_size-required_size;
if (pad_size && Config::GetHardware() != Config::HARDWARE_NGC)
{
p_pad=(uint8*)Mem::Malloc(pad_size);
for (uint32 i=0; i<pad_size; ++i)
{
p_pad[i]=0x69;
}
}
mc_man = Mc::Manager::Instance();
p_card=mc_man->GetCard(0,0);
if (!p_card)
{
goto ERROR;
}
// GameCube often crashes if try to do card operations on a bad card, so do this check first.
if (!p_card->IsFormatted())
{
goto ERROR;
}
char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
switch (Config::GetHardware())
{
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
if (!s_make_ps2_dir_and_icons(p_card,
file_type,p_name,
p_card_file_name,
&s_insufficient_space))
{
goto ERROR;
}
break;
case Config::HARDWARE_NGC:
{
// On the NGC, just generate the card file name.
// The icon cannot be created at this point because on the NGC the
// icon images have to be stored in the file data itself, so we have to
// wait until after the file is opened.
char p_directory_name[MAX_CARD_FILE_NAME_CHARS+1];
s_generate_card_directory_name(file_type,p_name,p_directory_name);
sprintf(p_card_file_name,"%s%s",p_directory_name,p_directory_name);
Dbg_MsgAssert(strlen(p_card_file_name)<MAX_CARD_FILE_NAME_CHARS+1,("Oops"));
break;
}
case Config::HARDWARE_XBOX:
if (!s_make_xbox_dir_and_icons(p_card,
file_type,p_name,
p_card_file_name,
&s_insufficient_space))
{
goto ERROR;
}
break;
default:
{
goto ERROR;
}
break;
}
// Open a file big enough to hold all the data.
//printf("Opening file %s of size %d for writing\n",p_card_file_name,fixed_size);
pFile=p_card->Open( p_card_file_name, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE, fixed_size );
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (!pFile)
{
goto ERROR;
}
pTemp=(uint8*)Mem::Malloc(header_and_structures_size);
p_file_header=(SMcFileHeader*)pTemp;
// Zero the whole structure to ensure that the XBox mSignature member is zeroed since
// it will be part of the data who's signature is calculated.
p_dest=(uint8*)p_file_header;
for (uint32 i=0; i<sizeof(SMcFileHeader); ++i)
{
*p_dest++=0;
}
if (Config::GetHardware()==Config::HARDWARE_NGC)
{
if (!s_insert_ngc_icon(p_file_header,p_card,pFile,file_type,p_name))
{
goto ERROR;
}
}
p_file_header->mVersion=s_get_version_number(file_type);
p_file_header->mSummaryInfoSize=summary_info_size;
p_file_header->mDataSize=header_and_structures_size;
p_dest=(uint8*)(p_file_header+1);
// Write in the summary info and calculate its checksum.
BytesWritten=WriteToBuffer(pSummaryInfo, p_dest, summary_info_size, Script::NO_ASSERT);
if (BytesWritten!=summary_info_size)
{
// Note: This assert is surrounded by an if to prevent a warning on non debug builds
// due to the BytesWritten variable being unused.
Dbg_MsgAssert(0,("BytesWritten does not equal summary_info_size ?"));
}
p_file_header->mSummaryInfoChecksum=Crc::GenerateCRCCaseSensitive((const char *)p_dest,summary_info_size);
p_dest+=summary_info_size;
// Write in the game save info
BytesWritten=WriteToBuffer(pMemCardStuff, p_dest, structure_size, Script::NO_ASSERT);
// K: Last minute fix, 'encrypt' the network settings so that the AOL info cannot be
// read. Not very secure, but it should be enough to make the strings non obvious.
if (file_type==0xca41692d) // NetworkSettings
{
//printf("Encrypting %d bytes ...\n",structure_size);
for (uint32 e=0; e<structure_size; ++e)
{
p_dest[e]+=0x69;
}
}
if (BytesWritten!=structure_size)
{
// Note: This assert is surrounded by an if to prevent a warning on non debug builds
// due to the BytesWritten variable being unused.
Dbg_MsgAssert(0,("BytesWritten does not equal structure_size ?"));
}
p_dest+=structure_size;
Dbg_MsgAssert(p_dest==pTemp+header_and_structures_size,("What ?"));
#ifdef __PLAT_XBOX__
HANDLE h_signature=XCalculateSignatureBegin(0);
if (XCalculateSignatureUpdate(h_signature,(const BYTE *)(pTemp),header_and_structures_size) != ERROR_SUCCESS)
{
Dbg_MsgAssert(0,("XCalculateSignatureUpdate failed!"));
}
if (pad_size)
{
Dbg_MsgAssert(p_pad,("NULL p_pad ?"));
if (XCalculateSignatureUpdate(h_signature,(const BYTE *)(p_pad),pad_size) != ERROR_SUCCESS)
{
Dbg_MsgAssert(0,("XCalculateSignatureUpdate failed!"));
}
}
if (XCalculateSignatureEnd(h_signature,&p_file_header->mSignature)!=ERROR_SUCCESS)
{
Dbg_MsgAssert(0,("XCalculateSignatureEnd failed!"));
}
#else
// Now calculate a checksum of all that data.
// It will include the contents of p_file_header in the data too,
// including mChecksum itself which will be zero at this point.
// By zeroing mChecksum and including it in the data that is checksum'd it
// means that the order of the members of SMcFileHeader does not matter.
// Just seems kind of ugly having to have mChecksum be the first member, cos
// it would also mean having to take the address of the second member to get
// the start address of the data to checksum, bleurgh.
p_file_header->mChecksum=Crc::GenerateCRCCaseSensitive((const char*)pTemp,header_and_structures_size);
#endif // #ifdef __PLAT_XBOX__
CardBytesWritten=pFile->Write( pTemp, header_and_structures_size );
//printf("Wrote %d bytes\n",CardBytesWritten);
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (CardBytesWritten!=header_and_structures_size)
{
goto ERROR;
}
#if __USE_REPLAYS__
// Write out the big replay buffer.
// It cannot be written out in one go by getting its base pointer, because on
// the GameCube it is stored in ARAM.
// The only way to access it is via the ReadFromBuffer function.
// Also, there is not enough memory to copy it into a big temporary buffer,
// so write it out in small chunks.
if (file_type==0x26c80b0d/* Replay */)
{
// Initialise the checksum, which is calculated cumulatively.
uint32 replay_data_checksum=0xffffffff;
// Write out the replay data header.
CardBytesWritten=pFile->Write( (uint8*)&replay_data_header, sizeof(Replay::SReplayDataHeader) );
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (CardBytesWritten!=sizeof(Replay::SReplayDataHeader))
{
goto ERROR;
}
replay_data_checksum=Crc::UpdateCRC((const char *)&replay_data_header,sizeof(Replay::SReplayDataHeader),replay_data_checksum);
// Write out the start-state dummies.
Replay::SSavedDummy saved_dummy;
Replay::CDummy *p_dummy=Replay::GetFirstStartStateDummy();
int count=0;
while (p_dummy)
{
p_dummy->Save(&saved_dummy);
CardBytesWritten=pFile->Write( (uint8*)&saved_dummy, sizeof(Replay::SSavedDummy) );
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (CardBytesWritten!=sizeof(Replay::SSavedDummy))
{
goto ERROR;
}
replay_data_checksum=Crc::UpdateCRC((const char *)&saved_dummy,sizeof(Replay::SSavedDummy),replay_data_checksum);
p_dummy=p_dummy->mpNext;
++count;
}
if (count!=replay_data_header.mNumStartStateDummies)
{
Dbg_MsgAssert(0,("Dummy count mismatch"));
}
int buf_size=Replay::GetBufferSize();
Dbg_MsgAssert((buf_size%REPLAY_BUFFER_CHUNK_SIZE)==0,("Replay buffer size not a multiple of REPLAY_BUFFER_CHUNK_SIZE"));
uint8 *p_chunk=Replay::GetTempBuffer();
int num_chunks=buf_size/REPLAY_BUFFER_CHUNK_SIZE;
uint32 offset=0;
for (int i=0; i<num_chunks; ++i)
{
Replay::ReadFromBuffer(p_chunk,offset,REPLAY_BUFFER_CHUNK_SIZE);
replay_data_checksum=Crc::UpdateCRC((const char *)p_chunk,REPLAY_BUFFER_CHUNK_SIZE,replay_data_checksum);
offset+=REPLAY_BUFFER_CHUNK_SIZE;
CardBytesWritten=pFile->Write( p_chunk, REPLAY_BUFFER_CHUNK_SIZE );
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (CardBytesWritten!=REPLAY_BUFFER_CHUNK_SIZE)
{
goto ERROR;
}
}
// Write out the replay data checksum
CardBytesWritten=pFile->Write( (uint8*)&replay_data_checksum, 4 );
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (CardBytesWritten!=4)
{
goto ERROR;
}
}
#endif
// Now write out padding so as to make sure the file has size fixed_size, otherwise
// there will be a discrepancy between the space that the game claims is required and
// the size that appears in the file list, and I'll get a ton of TT bugs.
if ( Config::GetHardware() != Config::HARDWARE_NGC)
{
// TODO: Fix bug in the Write function for the NGC. It always writes from offset 0,
// meaning that writing this padding overwrites the start of the file data.
// We don't actually have to write this padding on the NGC, because the resultant file size
// will be exactly that specified in the file Open command.
// But the bug will manifest itself when saving the replay data, so need to fix it ...
if (pad_size)
{
Dbg_MsgAssert(p_pad,("NULL p_pad ?"));
CardBytesWritten=pFile->Write( p_pad, pad_size );
//printf("Wrote %d bytes\n",CardBytesWritten);
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (CardBytesWritten != pad_size)
{
goto ERROR;
}
}
}
SavedOK=true;
ERROR:
if (p_pad)
{
Mem::Free(p_pad);
}
if (pTemp)
{
Mem::Free(pTemp);
}
if (pFile)
{
if (!pFile->Flush())
{
SavedOK=false;
}
if (!pFile->Close())
{
SavedOK=false;
}
delete pFile;
}
delete pSummaryInfo;
delete pMemCardStuff;
if (got_special_pools)
{
Dbg_MsgAssert(CComponent::SGetNumUsedItems()==0,("Expected the special mem card CComponent pool to be empty at this point, but got %d items",CComponent::SGetNumUsedItems()));
Dbg_MsgAssert(CStruct::SGetNumUsedItems()==0,("Expected the special mem card CStruct pool to be empty at this point, but got %d items",CStruct::SGetNumUsedItems()));
Dbg_MsgAssert(CVector::SGetNumUsedItems()==0,("Expected the special mem card CVector pool to be empty at this point, but got %d items",CVector::SGetNumUsedItems()));
CComponent::SSwitchToPreviousPool();
CStruct::SSwitchToPreviousPool();
CVector::SSwitchToPreviousPool();
}
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic( -1 );
Pcm::PauseStream( -1 );
}
if (Config::GetHardware() == Config::HARDWARE_NGC)
{
ScriptRemoveTemporaryMemCardPools(NULL,NULL);
}
return SavedOK;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
#if __USE_REPLAYS__
#ifdef __PLAT_NGC__
static SMcFileHeader sFileHeader __attribute__((aligned( 32 )));
#else
static SMcFileHeader sFileHeader;
#endif
#endif
bool ScriptSetSectionsToApplyWhenLoading(Script::CStruct *pParams, Script::CScript *pScript)
{
printf("ScriptSetSectionsToApplyWhenLoading was called.........................\n");
// These flags only apply when loading type OptionsAndPros.
// They allow for only certain sections of the file being applied to the game state.
// For example, when loading a custom skater, only the CUSTOM_SKATER part must be applied.
s_apply_flags=0;
if (pParams->ContainsFlag(CRCD(0x16b506c0,"ApplyCustomSkater")))
{
s_apply_flags |= 1<<APPLY_CUSTOM_SKATER;
}
if (pParams->ContainsFlag(CRCD(0xdc7ea3ce,"ApplyStorySkater")))
{
s_apply_flags |= 1<<APPLY_STORY_SKATER;
}
if (pParams->ContainsFlag(CRCD(0x33ec233f,"ApplyStory")))
{
s_apply_flags |= 1<<APPLY_STORY;
}
if (pParams->ContainsFlag(CRCD(0xb39d7cc4,"ApplyGeneralOptions")))
{
s_apply_flags |= 1<<APPLY_GLOBAL_INFO;
}
if (pParams->ContainsFlag(CRCD(0xc4e78e22,"all")))
{
s_apply_flags=0xffffffff;
}
return true;
}
// @script | LoadFromMemoryCard | Load the specified file from the memory card
// @parm string | Name | The name of the file to load
// @parm name | Type | The type of the file (cas, network, park, etc.)
bool ScriptLoadFromMemoryCard(Script::CStruct *pParams, Script::CScript *pScript)
{
Dbg_MsgAssert(!Config::Bootstrap(),("Can't use memory card from bootstrap demo"));
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic(true);
Pcm::PauseStream(true);
}
// Create structures for holding the summary info & game save info
CStruct *pSummaryInfo=new CStruct;
CStruct *pMemCardStuff=new CStruct;
// A bunch of variable declarations, which need to be up here cos there are goto's later
const char *p_name="";
uint32 file_type=0;
Mc::File* p_file=NULL;
int file_size=0;
uint8 *p_temp=NULL;
uint8 *p_stuff=NULL;
SMcFileHeader *p_file_header;
bool loaded_ok=false;
uint32 calculated_checksum=0;
uint32 stored_checksum=0;
bool checksum_matches=false;
// The LoadForUpload flag is passed as an integer so that that calls to LoadFromMemoryCard
// in script can set LoadForUpload equal to the value of some script global for convenience.
int load_for_upload=0;
pParams->GetInteger(CRCD(0x8c6808d4,"LoadForUpload"),&load_for_upload);
#ifdef __PLAT_XBOX__
XCALCSIG_SIGNATURE stored_signature;
XCALCSIG_SIGNATURE calculated_signature;
HANDLE h_signature;
BYTE *p_a=NULL;
BYTE *p_b=NULL;
#endif
// Get the card.
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (!p_card)
{
goto ERROR;
}
// GameCube often crashes if try to do card operations on a bad card, so do this check first.
if (!p_card->IsFormatted())
{
goto ERROR;
}
pParams->GetText("Name",&p_name);
pParams->GetChecksum("Type",&file_type);
// Calculate the low-level filename
char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
p_card_file_name[0]=0;
switch (Config::GetHardware())
{
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
{
const char *p_header=Config::GetMemCardHeader();
char p_ascii_checksum[20];
s_generate_ascii_checksum(p_ascii_checksum,p_name,file_type);
sprintf(p_card_file_name,"/%s%s/%s%s", p_header,p_ascii_checksum,
p_header,p_ascii_checksum);
break;
}
case Config::HARDWARE_NGC:
{
char p_directory_name[MAX_CARD_FILE_NAME_CHARS+1];
s_generate_card_directory_name(file_type,p_name,p_directory_name);
sprintf(p_card_file_name,"%s%s",p_directory_name,p_directory_name);
break;
}
case Config::HARDWARE_XBOX:
{
// Generate the directory name
const char *p_directory_name=s_generate_xbox_directory_name(file_type,p_name);
const char *p_low_level_directory_name=p_card->ConvertDirectory(p_directory_name);
if (!p_low_level_directory_name)
{
goto ERROR;
}
// Calculate the low-level file name.
sprintf( p_card_file_name, "/%s/%s", p_low_level_directory_name, p_low_level_directory_name );
break;
}
default:
{
goto ERROR;
break;
}
}
Dbg_MsgAssert(strlen(p_card_file_name)<MAX_CARD_FILE_NAME_CHARS+1,("Oops"));
#if __USE_REPLAYS__
if (file_type==0x26c80b0d/* Replay */)
{
strcpy(spReplayCardFileName,p_card_file_name);
sNeedToLoadReplayBuffer=true;
}
#endif
// Open the file.
p_file=p_card->Open( p_card_file_name, Mc::File::mMODE_READ );
if (!p_file)
{
// File could not be opened
goto ERROR;
}
// File opened OK
// Check the file size.
file_size=p_file->Seek( 0 ,Mc::File::BASE_END);
// Removed the file size check, because the fixed size needed to be updated, and keeping the
// check would prevent existing parks from being able to be loaded.
//if (file_size != (int)sGetFixedFileSize(file_type))
//{
// // Size mismatch, so consider the file corrupted.
// pScript->GetParams()->AddChecksum(NONAME,GenerateCRC("CorruptedData"));
// goto ERROR;
//}
// Read the file into memory
// Allocate a temporary buffer for reading the file into
p_temp=(uint8*)Mem::Malloc(file_size);
Dbg_MsgAssert(p_temp,("Could not allocate %d bytes for file buffer for file %s",file_size,p_card_file_name));
p_file->Seek(0,Mc::File::BASE_START);
if (p_file->Read(p_temp, file_size) != file_size)
{
// Some sort of read error.
goto ERROR;
}
// Seemed to read into memory OK
// Check if the data is corrupt by recalculating the checksum.
p_stuff=p_temp;
p_file_header=(SMcFileHeader*)p_stuff;
#ifdef __PLAT_XBOX__
// Load p_file_header->mSignature into stored_signature
// then zero out p_file_header->mSignature
p_a=p_file_header->mSignature.Signature;
p_b=stored_signature.Signature;
for (int i=0; i<XCALCSIG_SIGNATURE_SIZE; ++i)
{
*p_b=*p_a;
*p_a=0;
++p_a;
++p_b;
}
// Calculate the signature of the data.
h_signature=XCalculateSignatureBegin(0);
if (XCalculateSignatureUpdate(h_signature,(const BYTE *)p_stuff,file_size) != ERROR_SUCCESS)
{
Dbg_MsgAssert(0,("XCalculateSignatureUpdate failed!"));
}
if (XCalculateSignatureEnd(h_signature,&calculated_signature)!=ERROR_SUCCESS)
{
Dbg_MsgAssert(0,("XCalculateSignatureEnd failed!"));
}
// Compare the calculated signature with the stored one.
p_a=stored_signature.Signature;
p_b=calculated_signature.Signature;
checksum_matches=true;
for (int i=0; i<XCALCSIG_SIGNATURE_SIZE; ++i)
{
if (*p_a != *p_b)
{
checksum_matches=false;
break;
}
++p_a;
++p_b;
}
#else
checksum_matches=false;
stored_checksum=p_file_header->mChecksum;
// Set the checksum to zero because that was what it was when
// the checksum was calculated.
p_file_header->mChecksum=0;
// Using p_file_header->mDataSize instead of file_size, because file_size includes the
// padding, which is not included in the checksum calculation on PS2 and NGC
if (p_file_header->mDataSize > 500000)
{
// If mDataSize itself is corrupted, then don't go trying to calculate the
// checksum of megabytes of data, in case that hangs the game by hitting some sort
// of restricted memory location.
}
else
{
calculated_checksum=Crc::GenerateCRCCaseSensitive((const char*)p_stuff,p_file_header->mDataSize);
if (calculated_checksum==stored_checksum &&
p_file_header->mVersion==s_get_version_number(file_type))
{
checksum_matches=true;
}
}
#endif
if (checksum_matches)
{
// The data is OK, so it is safe to parse it.
// Skip over the header.
p_stuff=(uint8*)(p_file_header+1);
// Skip over the summary info
p_stuff=ReadFromBuffer(pSummaryInfo, p_stuff);
// K: Last minute fix, 'decrypt' the network settings.
if (file_type==0xca41692d) // NetworkSettings
{
int num_bytes=p_file_header->mDataSize-(p_stuff-((uint8*)p_file_header));
//printf("Decrypting %d bytes ...\n",num_bytes);
for (int e=0; e<num_bytes; ++e)
{
p_stuff[e]-=0x69;
}
}
// Read in the game save info
ReadFromBuffer(pMemCardStuff, p_stuff);
// The data is now safely in pMemCardStuff ...
if (load_for_upload)
{
// If the file was loaded for upload to the net, then do not process the data
// but just store it away so that the GetMemCardDataForUpload command can retrieve
// it a bit later.
Dbg_MsgAssert(spVaultData==NULL,("\n%s\nLoadFromMemoryCard expected the vault data to be cleared at this point ?",pScript->GetScriptInfo()));
spVaultData=new Script::CStruct;
spVaultData->AppendStructure(pMemCardStuff);
sVaultDataType=file_type;
if ( file_type == CRCD(0x61a1bc57,"cat") )
{
// load Created trick in edit slot so we can get some info out of it for uploading
s_read_game_save_info(file_type, pMemCardStuff, pScript);
}
}
else
{
// process the data according to the type of file loaded.
s_read_game_save_info(file_type, pMemCardStuff, pScript);
}
loaded_ok=true;
}
else
{
// Corrupted data, or bad version number
pScript->GetParams()->AddChecksum(NONAME,GenerateCRC("CorruptedData"));
}
// if the loading was successful,
// then run some post-load functions
if ( loaded_ok )
{
if (load_for_upload)
{
// If the file was loaded for upload to the net, then do not do any post processing,
// since the data has just been stored away for retrieval later.
}
else
{
Script::RunScript( "post_load_from_memory_card", pParams );
}
}
ERROR:
// Cleanup and unpause the music.
if (p_file)
{
p_file->Close();
delete p_file;
}
if (p_temp)
{
Mem::Free(p_temp);
}
delete pSummaryInfo;
delete pMemCardStuff;
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic( -1 );
Pcm::PauseStream( -1 );
}
return loaded_ok;
}
bool ScriptLoadedCustomSkater(Script::CStruct *pParams, Script::CScript *pScript)
{
return s_did_apply_custom_skater_info;
}
// Functions required for when loading a file of mem card for uploading to the net.
bool ScriptGetMemCardDataForUpload(Script::CStruct *pParams, Script::CScript *pScript)
{
Dbg_MsgAssert(spVaultData,("\n%s\nNo mem card data present",pScript->GetScriptInfo()));
pScript->GetParams()->AddStructure(CRCD(0x6d2ab6a,"DataForUpload"),spVaultData);
pScript->GetParams()->AddChecksum(CRCD(0x7321a8d6,"Type"),sVaultDataType);
return true;
}
bool ScriptClearMemCardDataForUpload(Script::CStruct *pParams, Script::CScript *pScript)
{
if (spVaultData)
{
delete spVaultData;
spVaultData=NULL;
}
sVaultDataType=0;
return true;
}
bool ScriptNeedToLoadReplayBuffer(Script::CStruct *pParams, Script::CScript *pScript)
{
#if __USE_REPLAYS__
return sNeedToLoadReplayBuffer;
#else
return false;
#endif
}
bool ScriptLoadReplayData(Script::CStruct *pParams, Script::CScript *pScript)
{
#if __USE_REPLAYS__
Dbg_MsgAssert(!Config::Bootstrap(),("Can't use memory card from bootstrap demo"));
Pcm::PauseMusic(true);
Pcm::PauseStream(true);
Dbg_MsgAssert(sNeedToLoadReplayBuffer,("Called LoadReplayBuffer when sNeedToLoadReplayBuffer is false"));
int replay_buffer_size=0;
Replay::SReplayDataHeader header;
Replay::SSavedDummy saved_dummy;
uint32 replay_data_checksum=0xffffffff; // Initialise the checksum, which is calculated accumulatively.
uint32 ch=0;
bool loaded_ok=false;
int bytes_read=0;
Mc::File* p_file=NULL;
uint8 *p_chunk=NULL;
int num_chunks=0;
int offset=0;
// Get the card.
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (!p_card)
{
goto ERROR;
}
// GameCube often crashes if try to do card operations on a bad card, so do this check first.
if (!p_card->IsFormatted())
{
goto ERROR;
}
// Open the file.
p_file=p_card->Open( spReplayCardFileName, Mc::File::mMODE_READ );
if (!p_file)
{
// File could not be opened
goto ERROR;
}
// File opened OK
// Seek to the start of the replay data by reading in the header, reading out the start data
// size from the header, then seeking there.
p_file->Seek( 0 ,Mc::File::BASE_START);
if (p_file->Read((uint8*)&sFileHeader, sizeof(SMcFileHeader)) != sizeof(SMcFileHeader))
{
// Some sort of read error.
goto ERROR;
}
p_file->Seek(sFileHeader.mDataSize,Mc::File::BASE_START);
// Make sure the buffer does exist
Replay::AllocateBuffer();
// and that it is cleared to start with.
Replay::ClearBuffer();
// Read in the header
bytes_read=p_file->Read( (uint8*)&header, sizeof(Replay::SReplayDataHeader) );
if (bytes_read!=sizeof(Replay::SReplayDataHeader))
{
// Some sort of read error.
goto ERROR;
}
Replay::ReadReplayDataHeader(&header);
replay_data_checksum=Crc::UpdateCRC((const char *)&header,sizeof(Replay::SReplayDataHeader),replay_data_checksum);
// Read in the dummies
for (int i=0; i<header.mNumStartStateDummies; ++i)
{
bytes_read=p_file->Read( (uint8*)&saved_dummy, sizeof(Replay::SSavedDummy) );
if (bytes_read!=sizeof(Replay::SSavedDummy))
{
// Some sort of read error.
goto ERROR;
}
replay_data_checksum=Crc::UpdateCRC((const char *)&saved_dummy,sizeof(Replay::SSavedDummy),replay_data_checksum);
Replay::CreateDummyFromSaveData(&saved_dummy);
}
// Read the data in a chunk at a time.
replay_buffer_size=Replay::GetBufferSize();
Dbg_MsgAssert((replay_buffer_size%REPLAY_BUFFER_CHUNK_SIZE)==0,("Replay buffer size not a multiple of REPLAY_BUFFER_CHUNK_SIZE"));
p_chunk=Replay::GetTempBuffer();
num_chunks=replay_buffer_size/REPLAY_BUFFER_CHUNK_SIZE;
offset=0;
for (int i=0; i<num_chunks; ++i)
{
bytes_read=p_file->Read( p_chunk, REPLAY_BUFFER_CHUNK_SIZE );
if (bytes_read!=REPLAY_BUFFER_CHUNK_SIZE)
{
// Some sort of read error.
goto ERROR;
}
Replay::WriteIntoBuffer(p_chunk,offset,REPLAY_BUFFER_CHUNK_SIZE);
replay_data_checksum=Crc::UpdateCRC((const char *)p_chunk,REPLAY_BUFFER_CHUNK_SIZE,replay_data_checksum);
offset+=REPLAY_BUFFER_CHUNK_SIZE;
}
// Read in and check the checksum
bytes_read=p_file->Read( (uint8*)&ch, 4 );
if (bytes_read!=4)
{
// Some sort of read error.
goto ERROR;
}
if (ch != replay_data_checksum)
{
goto ERROR;
}
// Hooray!
loaded_ok=true;
ERROR:
sNeedToLoadReplayBuffer=false;
if (p_file)
{
p_file->Close();
delete p_file;
}
Pcm::PauseMusic( -1 );
Pcm::PauseStream( -1 );
return loaded_ok;
#else
return false;
#endif
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool ScriptGetMaxTHPS4FilesAllowed(Script::CStruct *pParams, Script::CScript *pScript)
{
pScript->GetParams()->AddInteger("MaxTHPS4FilesAllowed",MAX_THPS4_FILES_ALLOWED);
return true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
static uint8 spSummaryInfoBuffer[sizeof(SMcFileHeader)+MAX_SUMMARY_INFO_SIZE];
bool ScriptGetMemCardDirectoryListing(Script::CStruct *pParams, Script::CScript *pScript)
{
pScript->GetParams()->RemoveComponent("DirectoryListing");
pScript->GetParams()->RemoveComponent("FilesLimitReached");
pScript->GetParams()->RemoveComponent("TotalTHPS4FilesOnCard");
uint32 file_type_to_list=0;
pParams->GetChecksum("FileType",&file_type_to_list);
// Get a directory listing of the whole card.
Mc::Manager * p_mc_man = Mc::Manager::Instance();
Mc::Card* p_card=p_mc_man->GetCard(0,0);
if (!p_card)
{
return true;
}
Lst::Head< Mc::File > file_list;
p_card->GetFileList( "*", file_list );
// If the special pools exist, switch to them so that the components & structs etc get allocated off them.
// The special pools use the space freed by unloading the skater anims, and are for preventing memory
// overflows when the save size gets large.
Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0,("Bad current CComponent pool"));
Dbg_MsgAssert(CStruct::SGetCurrentPoolIndex()==0,("Bad current CStruct pool"));
bool got_special_pools=false;
CComponent::SSwitchToNextPool();
if (CComponent::SPoolExists())
{
CStruct::SSwitchToNextPool();
Dbg_MsgAssert(CStruct::SPoolExists(),("No special CStruct pool ?"));
got_special_pools=true;
}
else
{
CComponent::SSwitchToPreviousPool();
}
int num_files_added=0;
CStruct *pp_structs[MAX_THPS4_FILES_ALLOWED];
// Run through all the files (well, directory names actually) and compare their
// first 12 characters with the THPS4 header.
const char *p_header=Config::GetMemCardHeader();
int header_len=strlen(p_header);
int num_files=file_list.CountItems();
int total_thps4_files=0;
for (int i=0; i<num_files; ++i)
{
Mc::File *p_file=file_list.GetItem(i)->GetData();
bool is_THPS4_file=true;
// No need to check the file name if it's the XBox because all the files
// will be in our filespace.
if ( Config::GetHardware() != Config::HARDWARE_XBOX )
{
for (int j=0; j<header_len; ++j)
{
if (p_file->m_Filename[j]!=p_header[j])
{
is_THPS4_file=false;
break;
}
}
}
if (!is_THPS4_file)
{
continue;
}
++total_thps4_files;
// It is a THPS4 file, so compare the file type with that requested.
uint32 file_type=0;
switch (Config::GetHardware())
{
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
case Config::HARDWARE_NGC:
// The filetype is indicated by the last letter of the 8 characters
// that follow the header. The other 7 contain the ascii checksum of
// the full file name as entered by the user.
file_type=s_determine_file_type(p_file->m_Filename[header_len+8-1]);
break;
case Config::HARDWARE_XBOX:
{
// TODO: Remove this ifdef somehow, needed currently because m_DisplayFilename
// is only defined for XBox. Shouldn't really have any platform ifdefs in this file.
#ifdef __PLAT_XBOX__
int length = strlen( p_file->m_DisplayFilename );
if( stricmp( &p_file->m_DisplayFilename[length - 15], "NetworkSettings" ) == 0 )
{
file_type = 0xca41692d; // NetworkSettings
}
else if( stricmp( &p_file->m_DisplayFilename[length - 6], "Skater" ) == 0 )
{
file_type = 0xb010f357; // OptionsAndPros
}
else if( stricmp( &p_file->m_DisplayFilename[length - 4], "Park" ) == 0 )
{
file_type = 0x3bf882cc; // Park
}
else if( stricmp( &p_file->m_DisplayFilename[length - 6], "Skater" ) == 0 )
{
file_type = 0xffc529f4; // Cas
}
else if( stricmp( &p_file->m_DisplayFilename[length - 5], "Trick" ) == 0 )
{
file_type = 0x61a1bc57; // CAT
}
else if( stricmp( &p_file->m_DisplayFilename[length - 6], "Replay" ) == 0 )
{
file_type = 0x26c80b0d; // Replay
}
else if( stricmp( &p_file->m_DisplayFilename[length - 5], "Goals" ) == 0 )
{
file_type = 0x62896edf; // CreatedGoals
}
#endif // #ifdef __PLAT_XBOX__
break;
}
case Config::HARDWARE_WIN32:
break;
default:
break;
}
if ( (file_type_to_list==0 && file_type!=0) ||
file_type==file_type_to_list)
{
if (num_files_added < MAX_THPS4_FILES_ALLOWED)
{
// It's safe to add a new entry to the array.
CStruct *p_struct=new CStruct;
p_struct->AddChecksum("file_type",file_type);
p_struct->AddInteger("Index",num_files_added);
p_struct->AddInteger("Year",p_file->m_Modified.m_Year);
p_struct->AddInteger("Month",p_file->m_Modified.m_Month);
p_struct->AddInteger("Day",p_file->m_Modified.m_Day);
p_struct->AddInteger("Hour",p_file->m_Modified.m_Hour);
p_struct->AddInteger("Minutes",p_file->m_Modified.m_Minutes);
p_struct->AddInteger("Seconds",p_file->m_Modified.m_Seconds);
char p_card_file_name[100];
if (Config::GetHardware()==Config::HARDWARE_NGC)
{
sprintf(p_card_file_name,"%s",p_file->m_Filename);
}
else
{
sprintf(p_card_file_name,"/%s/%s",p_file->m_Filename,p_file->m_Filename);
}
Dbg_MsgAssert(strlen(p_card_file_name)<100,("Oops"));
// Store the low level file name in the structure too so that the scripts
// can pass it on to the file delete function. This is necessary because
// even though the low level file name can be derived from the file name as
// stored in the file itself, if the file is corrupted the name might be too,
// so in that case it would not be possible to delete it.
p_struct->AddString("actual_file_name",p_card_file_name);
#ifdef __PLAT_XBOX__
p_struct->AddString("xbox_directory_name",p_file->m_DisplayFilename);
#endif
// Need to open the file to get the summary info out of it ...
Mc::File* p_mc_file=p_card->Open( p_card_file_name, Mc::File::mMODE_READ );
if (p_mc_file)
{
// File opened OK, so grab the first hundred or so bytes.
p_mc_file->Seek( 0 ,Mc::File::BASE_START);
p_mc_file->Read( spSummaryInfoBuffer, sizeof(SMcFileHeader)+MAX_SUMMARY_INFO_SIZE );
SMcFileHeader *p_file_header=(SMcFileHeader*)spSummaryInfoBuffer;
uint8 *p_summary_info=(uint8*)(p_file_header+1);
// Determine whether the summary info is corrupted.
bool corrupt_summary_info=false;
if (p_file_header->mSummaryInfoSize > MAX_SUMMARY_INFO_SIZE)
{
// The mSummaryInfoSize itself is corrupted!
corrupt_summary_info=true;
}
else
{
uint32 calculated_summary_info_checksum=Crc::GenerateCRCCaseSensitive((const char *)p_summary_info,p_file_header->mSummaryInfoSize);
if (calculated_summary_info_checksum != p_file_header->mSummaryInfoChecksum)
{
corrupt_summary_info=true;
}
}
// Calculate the total file size
int file_size=p_mc_file->Seek(0,Mc::File::BASE_END);
//printf("Directory listing: %s, size=%d\n",p_card_file_name,file_size);
int total_size_on_card=s_calculate_total_space_used_on_card(file_type,file_size);
p_struct->AddInteger("total_file_size",total_size_on_card);
// Removed the file size check, because the fixed size needed to be updated, and keeping the
// check would prevent existing parks from being able to be loaded.
//if (corrupt_summary_info || file_size != (int)sGetFixedFileSize(file_type))
if (corrupt_summary_info)
{
p_struct->AddChecksum(NONAME,Script::GenerateCRC("Corrupt"));
if (Config::GetHardware()==Config::HARDWARE_NGC)
{
p_struct->AddString("filename",GetString("NGCDamagedFile"));
}
else
{
p_struct->AddString("filename",GetString("DamagedFile"));
}
}
else if (p_file_header->mVersion!=s_get_version_number(file_type))
{
#ifdef __NOPT_ASSERT__
p_struct->AddChecksum(NONAME,Script::GenerateCRC("BadVersion"));
p_struct->AddString("filename",GetString("BadVersionNumber"));
#else
p_struct->AddChecksum(NONAME,Script::GenerateCRC("Corrupt"));
if (Config::GetHardware()==Config::HARDWARE_NGC)
{
p_struct->AddString("filename",GetString("NGCDamagedFile"));
}
else
{
p_struct->AddString("filename",GetString("DamagedFile"));
}
#endif
}
else
{
// Extract the summary info and stick it in the structure.
// This summary info gets printed at the bottom of the files menu when
// the highlight is on the file.
// The summary info is actually in the main structure stored in the file,
// but that would require loading in the whole thing which would take a
// long time.
// Have to use a temporary structure because ReadFromBuffer clears the
// passed structure first.
CStruct *p_temp=new CStruct;
ReadFromBuffer(p_temp,p_summary_info);
p_struct->AppendStructure(p_temp);
delete p_temp;
}
p_mc_file->Close();
delete p_mc_file;
}
else
{
// If the file did not open at all, treat it as corrupted.
p_struct->AddChecksum(NONAME,Script::GenerateCRC("Corrupt"));
p_struct->AddString("filename",GetString("DamagedFile"));
}
pp_structs[num_files_added++]=p_struct;
}
}
}
if (got_special_pools)
{
// Note: The pools will contain stuff at this point. The script will delete them
// when it does its cleanup.
CComponent::SSwitchToPreviousPool();
CStruct::SSwitchToPreviousPool();
}
// The file_list will probably get cleaned up when it goes out of scope, but just to be sure ...
file_list.DestroyAllNodes();
pScript->GetParams()->AddInteger("TotalTHPS4FilesOnCard",total_thps4_files);
// Set the FilesLimitReached flag so that the script can determine whether to add
// the 'Create New' entry to the files menu.
if (total_thps4_files >= MAX_THPS4_FILES_ALLOWED)
{
pScript->GetParams()->AddChecksum(NONAME,0x4eec27b5/*FilesLimitReached*/);
}
// If files were found, add the array to the script's params.
if (num_files_added)
{
// First, sort the array so that the files are definitely in date order (TT6112)
while (true)
{
bool did_a_swap=false;
for (int i=0; i<num_files_added-1; ++i)
{
if (s_first_date_is_more_recent(pp_structs[i+1],pp_structs[i]))
{
CStruct *p_temp=pp_structs[i];
pp_structs[i]=pp_structs[i+1];
pp_structs[i+1]=p_temp;
did_a_swap=true;
}
}
if (!did_a_swap)
{
break;
}
}
CArray *p_directory_listing_array=new CArray;
p_directory_listing_array->SetSizeAndType(num_files_added,ESYMBOLTYPE_STRUCTURE);
for (int f=0; f<num_files_added; ++f)
{
p_directory_listing_array->SetStructure(f,pp_structs[f]);
}
pScript->GetParams()->AddArrayPointer("DirectoryListing",p_directory_listing_array);
// Note: Not deleting the pp_structs[], cos they've been given to the array.
// Not deleting p_directory_listing_array, because it's been given to the scripts params.
}
return true;
}
// Returns true if the sector size is 8K, false otherwise.
// Needed as part of the GameCube card check procedure to check that the card is not some weird type.
// If it's a PS2 build, it will just return true.
bool ScriptSectorSizeOK(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGC__
Spt::SingletonPtr< Mc::Manager > mc_man;
Mc::Card* pCard=mc_man->GetCard(0,0);
if (pCard)
{
if (pCard->m_sector_size==8192)
{
return true;
}
else
{
return false;
}
}
return false;
#else
return true;
#endif
}
bool ScriptCardIsDamaged(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGC__
Spt::SingletonPtr< Mc::Manager > mc_man;
Mc::Card* pCard=mc_man->GetCard(0,0);
if (pCard)
{
return pCard->m_broken;
}
else
{
if (mc_man->GotFatalError())
{
return true;
}
}
return false;
#else
return false;
#endif
}
bool ScriptCardIsForeign(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGC__
Spt::SingletonPtr< Mc::Manager > mc_man;
Mc::Card* pCard=mc_man->GetCard(0,0);
if (pCard)
{
return pCard->IsForeign();
}
return false;
#else
return false;
#endif
}
bool ScriptBadDevice(Script::CStruct *pParams, Script::CScript *pScript)
{
#ifdef __PLAT_NGC__
Spt::SingletonPtr< Mc::Manager > mc_man;
Mc::Card* pCard=mc_man->GetCard(0,0);
if (pCard)
{
return false;
}
else
{
if (mc_man->GotWrongDevice())
{
return true;
}
}
return false;
#else
return false;
#endif
}
// For debugging, so that we can use the script debugger to view the contents of the save structure that would
// get saved out to the mem card.
bool ScriptGetSaveInfo(Script::CStruct *pParams, Script::CScript *pScript)
{
uint32 file_type=0;
pParams->GetChecksum(CRCD(0x7321a8d6,"type"),&file_type);
Script::CStruct *p_main_structure=new Script::CStruct;
s_insert_game_save_info(file_type, p_main_structure);
uint32 structure_size=CalculateBufferSize(p_main_structure);
Script::CStruct *p_summary_info=new Script::CStruct;
s_generate_summary_info(p_summary_info, file_type, p_main_structure);
uint32 summary_info_size=CalculateBufferSize(p_summary_info);
pScript->GetParams()->AddInteger(CRCD(0x172c1344,"MainStructureSize"),structure_size);
pScript->GetParams()->AddInteger(CRCD(0x96635475,"SummaryInfoSize"),summary_info_size);
pScript->GetParams()->AddInteger(CRCD(0x6afd2f7f,"MaxSummaryInfoSize"),MAX_SUMMARY_INFO_SIZE);
pScript->GetParams()->AddInteger(CRCD(0xecbb8262,"TotalSize"),sizeof(SMcFileHeader)+summary_info_size+structure_size);
pScript->GetParams()->AddInteger(CRCD(0xb35eb1d1,"MaxTotalSize"),sGetFixedFileSize(file_type));
pScript->GetParams()->AddStructurePointer(CRCD(0x703e60ca,"SummaryInfo"),p_summary_info);
pScript->GetParams()->AddStructurePointer(CRCD(0x671c9c00,"MainStructure"),p_main_structure);
return true;
}
// It is safe to call this multiple times.
bool ScriptCreateTemporaryMemCardPools(Script::CStruct *pParams, Script::CScript *pScript)
{
// If the pools exist already, do nothing.
CComponent::SSwitchToNextPool();
if (CComponent::SPoolExists())
{
CComponent::SSwitchToPreviousPool();
CStruct::SSwitchToNextPool();
Dbg_MsgAssert(CStruct::SPoolExists(),("Expected CStruct pool to exist"));
CStruct::SSwitchToPreviousPool();
CVector::SSwitchToNextPool();
Dbg_MsgAssert(CVector::SPoolExists(),("Expected CVector pool to exist"));
CVector::SSwitchToPreviousPool();
return true;
}
CComponent::SSwitchToPreviousPool();
#ifdef __PLAT_NGC__
#define NUM_COM 16000
#define NUM_STR 8000
#define NUM_VEC 3000
#define BUFFER_SIZE ( ( NUM_COM * 16 ) + ( NUM_STR * 8 ) + ( NUM_VEC * 16 ) )
// Time for a hack...
g_mc_hack = true;
g_hack_address = NsARAM::alloc( BUFFER_SIZE );
if ( g_hack_address )
{
NsDMA::toARAM( g_hack_address, g_p_buffer, BUFFER_SIZE );
}
memset( g_p_buffer, 0, BUFFER_SIZE );
NsBuffer::reset( false );
#else
#define NUM_COM 50000
#define NUM_STR 50000
#define NUM_VEC 10000
#endif // __PLAT_NGC__
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
Mem::PushMemProfile("Mem card CComponent");
Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0,("Bad current CComponent pool"));
CComponent::SSwitchToNextPool();
CComponent::SCreatePool(NUM_COM, "Reserve CComponent");
CComponent::SSwitchToPreviousPool();
Mem::PopMemProfile();
Mem::PushMemProfile("Mem card CStruct");
Dbg_MsgAssert(CStruct::SGetCurrentPoolIndex()==0,("Bad current CStruct pool"));
CStruct::SSwitchToNextPool();
CStruct::SCreatePool(NUM_STR, "Reserve CStruct");
CStruct::SSwitchToPreviousPool();
Mem::PopMemProfile();
// 12 bytes each (Actually 16)
Mem::PushMemProfile("Mem card CVector");
Dbg_MsgAssert(CVector::SGetCurrentPoolIndex()==0,("Bad current CVector pool"));
CVector::SSwitchToNextPool();
CVector::SCreatePool(NUM_VEC, "Reserve CVector");
CVector::SSwitchToPreviousPool();
Mem::PopMemProfile();
Mem::Manager::sHandle().PopContext();
return true;
}
// It is safe to call this multiple times.
bool ScriptRemoveTemporaryMemCardPools(Script::CStruct *pParams, Script::CScript *pScript)
{
Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0,("Bad current CComponent pool"));
CComponent::SSwitchToNextPool();
CComponent::SRemovePool(); // Does nothing if the pool does not exist.
CComponent::SSwitchToPreviousPool();
Dbg_MsgAssert(CStruct::SGetCurrentPoolIndex()==0,("Bad current CStruct pool"));
CStruct::SSwitchToNextPool();
CStruct::SRemovePool();
CStruct::SSwitchToPreviousPool();
Dbg_MsgAssert(CVector::SGetCurrentPoolIndex()==0,("Bad current CVector pool"));
CVector::SSwitchToNextPool();
CVector::SRemovePool();
CVector::SSwitchToPreviousPool();
#ifdef __PLAT_NGC__
if ( g_hack_address )
{
NsDMA::toMRAM( g_p_buffer, g_hack_address, BUFFER_SIZE );
}
NsARAM::free( g_hack_address );
g_mc_hack = false;
NsBuffer::reset( true );
#endif // __PLAT_NGC__
return true;
}
bool ScriptSwitchToTempPoolsIfTheyExist(Script::CStruct *pParams, Script::CScript *pScript)
{
Dbg_MsgAssert(CComponent::SGetCurrentPoolIndex()==0 && CStruct::SGetCurrentPoolIndex()==0 && CVector::SGetCurrentPoolIndex()==0, ("Expected current pools to be 0"));
CComponent::SSwitchToNextPool();
CStruct::SSwitchToNextPool();
CVector::SSwitchToNextPool();
if (CComponent::SPoolExists() && CStruct::SPoolExists() && CVector::SPoolExists())
{
return true;
}
CComponent::SSwitchToPreviousPool();
CStruct::SSwitchToPreviousPool();
CVector::SSwitchToPreviousPool();
return false;
}
bool ScriptSwitchToRegularPools(Script::CStruct *pParams, Script::CScript *pScript)
{
if (CComponent::SGetCurrentPoolIndex()==0 &&
CStruct::SGetCurrentPoolIndex()==0 &&
CVector::SGetCurrentPoolIndex()==0)
{
return true;
}
CComponent::SSwitchToPreviousPool();
CStruct::SSwitchToPreviousPool();
CVector::SSwitchToPreviousPool();
return true;
}
// Saves any old data file to mem card
bool SaveDataFile(const char *p_name, uint8 *p_data, uint32 size)
{
Dbg_MsgAssert(p_name,("NULL p_name"));
Dbg_MsgAssert(p_data,("NULL p_data"));
switch (Config::GetHardware())
{
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
break;
default:
return false;
break;
}
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic(true);
Pcm::PauseStream(true);
}
Mc::File* pFile=NULL;
char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
const char *p_header=Config::GetMemCardHeader();
char p_ascii_checksum[20];
int suffix=0;
#define FULL_NAME_BUF_SIZE 100
char p_full_name[FULL_NAME_BUF_SIZE];
bool SavedOK=false;
uint32 CardBytesWritten=0;
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (!p_card)
{
goto ERROR;
}
while (true)
{
Dbg_MsgAssert(strlen(p_name)<FULL_NAME_BUF_SIZE-3,("p_name too long"));
sprintf(p_full_name,"%s%03d",p_name,suffix);
s_generate_ascii_checksum(p_ascii_checksum,p_full_name,0xb010f357/*OptionsAndPros*/);
sprintf(p_card_file_name,"/%s%s/%s%s", p_header,p_ascii_checksum,
p_header,p_ascii_checksum);
Dbg_MsgAssert(strlen(p_card_file_name)<MAX_CARD_FILE_NAME_CHARS,("Oops"));
pFile = p_card->Open( p_card_file_name, Mc::File::mMODE_READ );
if (pFile)
{
pFile->Flush();
pFile->Close();
delete pFile;
pFile=NULL;
}
else
{
break;
}
++suffix;
Dbg_MsgAssert(suffix<1000,("Too many files!!"));
}
printf ("Creating mem card file '%s'\n",p_full_name);
if (!s_make_ps2_dir_and_icons(p_card,
0xb010f357, // OptionsAndPros tee hee
p_full_name,
p_card_file_name,
&s_insufficient_space))
{
goto ERROR;
}
// Open a file big enough to hold all data
pFile=p_card->Open( p_card_file_name, Mc::File::mMODE_WRITE | Mc::File::mMODE_CREATE, s_round_up_to_platforms_block_size(size) );
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (!pFile)
{
goto ERROR;
}
CardBytesWritten=pFile->Write( p_data, size );
s_insufficient_space=p_card->GetLastError()==Mc::Card::vINSUFFICIENT_SPACE;
if (CardBytesWritten!=size)
{
goto ERROR;
}
SavedOK=true;
ERROR:
if (pFile)
{
if (!pFile->Flush())
{
SavedOK=false;
}
if (!pFile->Close())
{
SavedOK=false;
}
delete pFile;
}
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic( -1 );
Pcm::PauseStream( -1 );
}
return SavedOK;
}
// Load len bytes of any old data file to mem card to p_data
bool LoadDataFile(const char *p_name, uint8 *p_data, uint32 size)
{
Dbg_MsgAssert(p_name,("NULL p_name"));
Dbg_MsgAssert(p_data,("NULL p_data"));
switch (Config::GetHardware())
{
case Config::HARDWARE_PS2:
case Config::HARDWARE_PS2_PROVIEW:
case Config::HARDWARE_PS2_DEVSYSTEM:
break;
default:
return false;
break;
}
if ( Config::GetHardware() != Config::HARDWARE_XBOX)
{
Pcm::PauseMusic(true);
Pcm::PauseStream(true);
}
Mc::File* pFile=NULL;
char p_card_file_name[MAX_CARD_FILE_NAME_CHARS+1];
const char *p_header=Config::GetMemCardHeader();
char p_ascii_checksum[20];
#define FULL_NAME_BUF_SIZE 100
char p_full_name[FULL_NAME_BUF_SIZE];
Mc::Manager * mc_man = Mc::Manager::Instance();
Mc::Card* p_card=mc_man->GetCard(0,0);
if (!p_card)
{
return false;;
}
Dbg_MsgAssert(strlen(p_name)<FULL_NAME_BUF_SIZE-3,("p_name too long"));
sprintf(p_full_name,"%s",p_name);
s_generate_ascii_checksum(p_ascii_checksum,p_full_name,0xb010f357/*OptionsAndPros*/);
sprintf(p_card_file_name,"/%s%s/%s%s", p_header,p_ascii_checksum,
p_header,p_ascii_checksum);
Dbg_MsgAssert(strlen(p_card_file_name)<MAX_CARD_FILE_NAME_CHARS,("Oops"));
pFile = p_card->Open( p_card_file_name, Mc::File::mMODE_READ );
if (pFile)
{
printf ("Loading From Memory Card\n");
pFile->Flush();
pFile->Seek( 0 ,Mc::File::BASE_START);
pFile->Read(p_data,size);
pFile->Flush();
pFile->Close();
delete pFile;
return true;
}
return false;
}
} // namespace CFuncs