//**************************************************************************** //* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __PLAT_NGPS__ #include #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 'ß': ascii_code='B'; break; case 'Ä': ascii_code='A'; break; case 'Ü': ascii_code='U'; break; case 'Ö': ascii_code='O'; break; case 'à': ascii_code='a'; break; case 'â': ascii_code='a'; break; case 'ä': ascii_code='a'; break; case 'ê': ascii_code='e'; break; case 'è': ascii_code='e'; break; case 'é': ascii_code='e'; break; case 'ë': ascii_code='e'; break; case 'ì': ascii_code='i'; break; case 'î': ascii_code='i'; break; case 'ï': ascii_code='i'; break; case 'ô': ascii_code='o'; break; case 'ò': ascii_code='o'; break; case 'ö': ascii_code='o'; break; case 'ù': ascii_code='u'; break; case 'û': ascii_code='u'; break; case 'ü': ascii_code='u'; break; case 'ç': ascii_code='c'; break; case 'œ': 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; imp_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"),¤t_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; iGetStructure(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"),¤t_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<GetStructure(CRCD(0xf55cbd13,"GlobalInfo"),&p_global_info,Script::ASSERT); s_read_global_info(p_global_info, p_script); } if (s_apply_flags & (1<GetStructure(CRCD(0x14a9fbc7,"Story"),&p_story_info,Script::ASSERT); s_read_story_info(p_story_info); } if (s_apply_flags & (1<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<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)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)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>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)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; iGetSize(); ++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)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)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)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; iGetCard(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)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; imVersion=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; emSignature)!=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; iWrite( 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<ContainsFlag(CRCD(0xdc7ea3ce,"ApplyStorySkater"))) { s_apply_flags |= 1<ContainsFlag(CRCD(0x33ec233f,"ApplyStory"))) { s_apply_flags |= 1<ContainsFlag(CRCD(0xb39d7cc4,"ApplyGeneralOptions"))) { s_apply_flags |= 1<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)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; imChecksum; // 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; eGetScriptInfo())); 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; iRead( (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; iRead( 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; iGetData(); 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; jm_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; iSetSizeAndType(num_files_added,ESYMBOLTYPE_STRUCTURE); for (int f=0; fSetStructure(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)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)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