// Title : FileMgr.cpp // Author : Adam Fowler // Started : 22/02/05 #include "system/FileMgr.h" // C headers #if __WIN32PC #include "direct.h" #pragma warning(disable: 4668) #include #pragma warning(error: 4668) #endif // Rage headers #include "bank/bank.h" #include "bank/bkmgr.h" #include "bank/bank.h" #include "diag/channel.h" #include "file/asset.h" #include "file/file_channel.h" #include "file/default_paths.h" #include "file/device_crc.h" #include "file/device_installer.h" #include "file/device_relative.h" #include "file/packfile.h" #include "file/savegame.h" #include "grcore/debugdraw.h" #include "grcore/light.h" #include "grcore/setup.h" #include "grcore/stateblock.h" #include "grmodel/setup.h" #include "parser/manager.h" #include "profile/timebars.h" #include "streaming/CacheLoader.h" #include "streaming/packfilemanager.h" #include "streaming/streamingengine.h" #include "streaming/streaminginstall_psn.h" #include "streaming/streamingvisualize.h" #include "string/string.h" #include "system/bootmgr.h" #include "system/memory.h" #include "system/system.h" #include "system/xtl.h" #include "system/param.h" #include "audiohardware/waveslot.h" // Game headers #ifndef NAVGEN_TOOL #include "audio/frontendaudioentity.h" #include "control/gamelogic.h" #include "control/videorecording/videorecording.h" #include "frontend/GameStreamMgr.h" #include "frontend/loadingscreens.h" #include "frontend/WarningScreen.h" #include "modelinfo/ModelInfo.h" #include "renderer/color.h" #include "renderer/renderthread.h" #include "scene/datafilemgr.h" #include "scene/extracontent.h" #include "scene/scene.h" #include "system/controlmgr.h" #include "system/StreamingInstall.winrt.h" #include "system/system.h" #include "text/Text.h" #include "text/TextConversion.h" #include "debug/Debug.h" #else #include #include "GTATypes.h" #endif #define ASSETS_DIRECTORY "x:/gta5/assets_ng/" #define ART_DIRECTORY RS_PROJROOT "/art/" #if __BANK #define TOOL_DIRECTORY RS_TOOLSROOT "/" #endif // Max number of RPFs the platform packfile can be split up into #if RSG_ORBIS || RSG_DURANGO || (RSG_PC && RSG_CPU_X64) #define MAX_PLATFORM_PACK_COUNT 26 #define MAX_PLATFORMCRC_PACK_COUNT 2 // only one needed right now, but we might add more later, eg, if split across chunks #else #define MAX_PLATFORM_PACK_COUNT 2 #endif // RSG_ORBIS || RSG_DURANGO #define USE_NUMBERED_PLATPACKS 0 // use numbered platform packs instead of lettered, eg, xboxone02.rpf instead of xboxonec.rpf (if needing more than 26 packs) // The PLATFORM_DIRECTORY needs to be consistent with the tests in ragebuilder/pack.cpp. #if RSG_ORBIS #define DISKROOT_DIRECTORY "/app0/" #define HDD_GAME_DIRECTORY_BASE "/data/app/" #if __FINAL #define ROOT_DIRECTORY "/app0/" #else #define ROOT_DIRECTORY RS_BUILDBRANCH "/" #endif // __FINAL #define PLATFORM_PACK_COUNT 23 #define PLATFORMCRC_PACK_COUNT 1 #define PLATFORM_DIRECTORY "ps4/" #define PLATFORM_PACK "ps4.rpf" #define PLATFORM_DEBUG_PACK "ps4_debug.rpf" // contains all unshippable data for packaged non-final builds #elif RSG_DURANGO #define DISKROOT_DIRECTORY "G:/" #define HDD_GAME_DIRECTORY_BASE "G:/" #if __FINAL #define ROOT_DIRECTORY "G:/" #else #define ROOT_DIRECTORY RS_BUILDBRANCH "/" #endif // __FINAL #define PLATFORM_PACK_COUNT 23 #define PLATFORMCRC_PACK_COUNT 1 #define PLATFORM_DIRECTORY "xboxone/" #define PLATFORM_PACK "xboxone.rpf" #define PLATFORM_DEBUG_PACK "xboxone_debug.rpf" // contains all unshippable data for packaged non-final builds #else // pc, anything else #define DISKROOT_DIRECTORY "" #if __PACKEDDEBUG #define ROOT_DIRECTORY "" #else #define ROOT_DIRECTORY RS_BUILDBRANCH "/" #endif #define PLATFORM_PACK_COUNT 23 #define PLATFORMCRC_PACK_COUNT 1 #if __64BIT #define PLATFORM_DIRECTORY "x64/" #define PLATFORM_PACK "x64.rpf" #define PLATFORM_DEBUG_PACK "x64_debug.rpf" // contains all unshippable data for packaged non-final builds #else #define PLATFORM_DIRECTORY "pc/" #define PLATFORM_PACK "pc.rpf" #endif #endif //__XENON #if !__PACKEDDEBUG #define COMMON_DEBUG_PACK "common_debug.rpf" // contains all unshippable data for packaged non-final builds #endif // !__FINAL #define COMMON_DIRECTORY "common/" #if RSG_ORBIS #define AUDIO_DIRECTORY "ps4/audio/" #elif RSG_DURANGO #define AUDIO_DIRECTORY "xboxone/audio/" #elif RSG_PC #define AUDIO_DIRECTORY "x64/audio/" #else #define AUDIO_DIRECTORY PLATFORM_DIRECTORY "audio/" #endif #if !__PACKEDDEBUG #define AUDIO_RPF "audio.rpf" #define AUDIO_PACKLIST "packlist.txt" #else #define AUDIO_RPF "audio_rel.rpf" #define AUDIO_PACKLIST "packlist_rel.txt" #endif char CFileMgr::ms_rootDirName[MAX_DIRNAME_CHARS] = ROOT_DIRECTORY; char CFileMgr::ms_dirName[MAX_DIRNAME_CHARS]; char CFileMgr::ms_hddBootPath[MAX_DIRNAME_CHARS] = ""; #if __WIN32PC atFixedString CFileMgr::ms_appPath; atFixedString CFileMgr::ms_userPath; atFixedString CFileMgr::ms_rgscPath; atFixedString CFileMgr::ms_appDataRootDirName; atFixedString CFileMgr::ms_originalAppDataRootDirName; atFixedString CFileMgr::ms_userDataRootDirName; atFixedString CFileMgr::ms_originalUserDataRootDirName; atFixedString CFileMgr::ms_rockStarRootDirName; static sysCriticalSectionToken s_FileMgrToken; #endif // __WIN32PC bool CFileMgr::ms_allChunksOnHdd = true; #if !__FINAL struct DummyAlloc { void * m_Buffer; u32 m_Size; u32 m_Allocator; }; DummyAlloc s_dummyAllocs[] = { // orbis, durango, pc // keep in sync VIRTUAL_STREAMING_MEMORY_LIMIT and PHYSICAL_STREAMING_MEMORY_LIMIT (GameScriptMgr.h) { NULL, 1 << 23, MEMTYPE_RESOURCE_VIRTUAL }, { NULL, 1 << 23, MEMTYPE_RESOURCE_VIRTUAL }, { NULL, 1 << 22, MEMTYPE_RESOURCE_VIRTUAL }, //4 { NULL, 1 << 23, MEMTYPE_RESOURCE_PHYSICAL }, { NULL, 1 << 23, MEMTYPE_RESOURCE_PHYSICAL }, { NULL, 1 << 23, MEMTYPE_RESOURCE_PHYSICAL }, { NULL, 1 << 23, MEMTYPE_RESOURCE_PHYSICAL }, { NULL, 1 << 23, MEMTYPE_RESOURCE_PHYSICAL }, { NULL, 1 << 23, MEMTYPE_RESOURCE_PHYSICAL }, //8 { NULL, 1 << 22, MEMTYPE_RESOURCE_PHYSICAL }, //4 { NULL, 1 << 21, MEMTYPE_RESOURCE_PHYSICAL }, //2 { NULL, 1 << 20, MEMTYPE_RESOURCE_PHYSICAL } //1 }; #endif //!__FINAL fiDeviceRelative gPlatformDevice; fiDeviceRelative gCommonDevice; fiDeviceRelative gGameCacheDevice; fiDeviceRelative gAudioDevice; fiDeviceCrc gCrcPlatformDevice; fiDeviceCrc gCrcCommonDevice; #if !__PACKEDDEBUG fiDeviceRelative gAssetsDevice; fiDeviceRelative gArtDevice; fiDeviceRelative gDebugDevice; // mounted as debug: typically from the hosted build directory (eg, //build/) // For packaged non-final builds, the devices below mount extra 'xxx_debug.rpf' packfiles that have -all- unshippable data in them // To disable these (eg, for simulating a final configuration), use -nodebugpack as well as any applicable redirects (only final shaders, scripts, and text will be available) // Note that these are mounted after the main devices, but before the update: device, so if there's any overlap, the search hierarchy is update: -> debug common:/platform: -> main common:/platform: fiPackfile gDebugPlatformPackfileDevice; // only for packaged non-final builds (mounted as platform: from {platform}_debug.rpf) fiPackfile gDebugCommonPackfileDevice; // only for packaged non-final builds (mounted as common: from common_debug.rpf) #if __BANK fiDeviceRelative gToolDevice; #endif // __BANK #if RSG_ORBIS || RSG_DURANGO fiDeviceRelative gPlatformDeviceRfs; fiDeviceRelative gCommonDeviceRfs; fiDeviceCrc gCrcPlatformDeviceRfs; fiDeviceCrc gCrcCommonDeviceRfs; #endif // RSG_ORBIS || RSG_DURANGO #endif // !__FINAL #if __WIN32PC fiDeviceRelative gUserDevice; fiDeviceRelative gAppDataDevice; #endif // __WIN32PC fiPackfile gCommonPackfileDevice; fiPackfile gPlatformPackfileDevice[MAX_PLATFORM_PACK_COUNT]; fiDeviceCrc gPlatformCrcPackfileDevice[MAX_PLATFORMCRC_PACK_COUNT]; fiPackfile gAudioPackfileDevice; atArray gAudioPacks; int gPlatformPackfileCount = PLATFORM_PACK_COUNT; int gPlatformCRCPackfileCount = PLATFORMCRC_PACK_COUNT; #define PLAYGO_PACK_COUNT (2) bool gCommonRpfInMemory = false; bool gCommonRpfFromOdd = true; namespace rage { NOSTRIP_XPARAM(audiofolder); } PARAM(marketing, "Marketing memory size"); PARAM(maponly, "Only load the map - turn off all cars & peds"); PARAM(maponlyextra, "Only load the map - turn off all cars & peds"); PARAM(stealextramemory, "Steal some extra memory"); PARAM(cheapmode, "Only load the map - turn off all cars & peds - no water reflections - reflection slods=300 - far clip=1500"); PARAM(common, "Indicate where common folder is"); PARAM(platform, "Indicate where platform folder is"); PARAM(nodebugpack, "Disable use of xxx_debug.rpf packfiles in packaged non-final builds"); NOSTRIP_PARAM(commonpack, "Indicate where common packfile is"); NOSTRIP_PARAM(platformpack, "Indicate where platform packfile is"); NOSTRIP_PARAM(audiopack, "Indicate where audio packfile is"); PARAM(audiopacklist, "Override audio packlist location"); PARAM(artdir, "Indicate where the art dir for the game is"); PARAM(assetsdir, "Indicate where the assets dir for the game is"); #if __BANK PARAM(tooldir, "Indicate where the tools dir for the game is"); #endif PARAM(rootdir, "Indicate where the root dir for the game is"); PARAM(usepackfiles, "Use RPF pack files"); PARAM(forceadpcm, "Use mp3 files for PS4"); #if defined(VIDEO_RECORDING_ENABLED) && VIDEO_RECORDING_ENABLED && (RSG_PC || RSG_DURANGO) PARAM(videoOutputDir, "Indicate where to place recorded videos"); #endif NOSTRIP_PARAM(userdir, "Indictate where the user folder is"); PARAM(cleaninstall, "Do not use hdd data even when it's newer."); PARAM(playgoemu, "Emulation of PlayGo through private http server"); PARAM(playgotest, "Triggers the PlayGo script for debug and testing"); #if RSG_DURANGO || RSG_ORBIS namespace rage { XPARAM(forceboothdd); XPARAM(forceboothddloose); }; #endif static fiPackfile::CacheMode GetDefaultPackfileCacheMode() { return fiDeviceInstaller::GetIsBootedFromHdd() ? fiPackfile::CACHE_INSTALLER_FILE : fiPackfile::CACHE_NONE; } const char* GetRootFolder() { const char* pRootFolder = ROOT_DIRECTORY; #if __CONSOLE if (fiDeviceInstaller::GetIsBootedFromHdd()) { return CFileMgr::GetHddBootPath(); } if(sysBootManager::IsBootedFromDisc()) { return DISKROOT_DIRECTORY; } #endif if(PARAM_rootdir.Get(pRootFolder)) { fileAssertf((pRootFolder[strlen(pRootFolder)-1] == '/'), "-rootdir parameter requires terminating //!"); } #if RSG_DURANGO && __FINAL return "G:/"; #else #if RSG_DURANGO if(PARAM_forceboothdd.Get() || PARAM_forceboothddloose.Get()) { return "G:/"; } else #endif { #if !__FINAL fileDisplayf("Root folder %s\n", pRootFolder); #endif return pRootFolder; } #endif } const char *GetAudioFolder() { if (fiDeviceInstaller::GetIsBootedFromHdd()) { #if RSG_DURANGO || RSG_ORBIS return HDD_GAME_DIRECTORY_BASE AUDIO_DIRECTORY; #else return GetRootFolder(); #endif } if(sysBootManager::IsBootedFromDisc()) { #if RSG_DURANGO || RSG_ORBIS return DISKROOT_DIRECTORY AUDIO_DIRECTORY; #else return GetRootFolder(); #endif } #if RSG_ORBIS if(PARAM_forceadpcm.Get()) { return "x64/audio/"; } else { return "ps4/audio/"; } #elif RSG_DURANGO return "xboxone/audio/"; #elif RSG_PC return "x64/audio/"; #else return "platform:/audio/"; #endif } namespace rage { NOSTRIP_XPARAM(nocachetimestamps); }; #if !__FINAL namespace rage { XPARAM(noquits); XPARAM(update); }; #endif const char* CFileMgr::GetAssetsFolder() { const char* pAssetsFolder = ASSETS_DIRECTORY; if(PARAM_assetsdir.Get(pAssetsFolder)) { fileAssertf((pAssetsFolder[strlen(pAssetsFolder)-1] == '/'), "-assetsdir parameter requires terminating //!"); } #if !__FINAL fileDisplayf("Assets folder %s\n", pAssetsFolder); #endif return pAssetsFolder; } const char* GetArtFolder() { const char* pArtFolder = ART_DIRECTORY; if(PARAM_artdir.Get(pArtFolder)) { fileAssertf((pArtFolder[strlen(pArtFolder)-1] == '/'), "-artdir parameter requires terminating //!"); } #if !__FINAL fileDisplayf("Art folder %s\n", pArtFolder); #endif return pArtFolder; } #if __BANK const char* CFileMgr::GetToolFolder() { const char* pToolFolder = TOOL_DIRECTORY; if(PARAM_tooldir.Get(pToolFolder)) { fileAssertf((pToolFolder[strlen(pToolFolder)-1] == '/'), "-tooldir parameter requires terminating //!"); } #if !__FINAL fileDisplayf("Tool folder %s\n", pToolFolder); #endif return pToolFolder; } #endif // // name: ReadErrorHandler // description: Read error handler void ReadErrorHandler() { #if RSG_ORBIS #if __FINAL CWarningScreen::SetFatalReadMessageWithHeader(WARNING_MESSAGE_STANDARD, "", "READ_ERROR", FE_WARNING_NONE, false, -1); #else CWarningScreen::SetFatalReadMessageWithHeader(WARNING_MESSAGE_STANDARD, "", "READ_ERROR", FE_WARNING_OK, false, -1); #endif #endif } void CheckPack(bool success, const char *OUTPUT_ONLY(filename)) { //@@: location CFILEMGR_CHECKPACK_ENTRY if(!success) { if(sysBootManager::IsBootedFromDisc()) { Errorf("Unable to find critical archive '%s', your disc image might be broken.",filename); ReadErrorHandler(); PS3_ONLY(Quitf("Missing packfile" OUTPUT_ONLY("%s", filename))); } else { fileErrorf("Unable to find critical archive '%s', check that RFS connected properly.",filename); __debugbreak(); ReadErrorHandler(); } } } #if !__FINAL && !defined(NAVGEN_TOOL) // allocate the dummy chunks of memory to prevent art mode using up all resource memory void CFileMgr::AllocateDummyResourceMem() { USE_MEMBUCKET(MEMBUCKET_SYSTEM); MEM_USE_USERDATA(MEMUSERDATA_TEXLITE); for(u32 i=0; i s_extraallocs; // allocate the dummy chunks of memory to prevent art mode using up all resource memory void CFileMgr::AllocateExtraResourceMem(size_t memory) { USE_MEMBUCKET(MEMBUCKET_SYSTEM); MEM_USE_USERDATA(MEMUSERDATA_TEXLITE); strStreamingAllocator& allocator = strStreamingEngine::GetAllocator(); size_t taken = 0; size_t chunk = g_rscVirtualLeafSize << 8; while (memory > g_rscVirtualLeafSize) { while (chunk > memory) { chunk >>= 1; } if (chunk < g_rscVirtualLeafSize) break; void* alloc = allocator.TryAllocate(chunk, 128, MEMTYPE_RESOURCE_VIRTUAL); if (alloc) { s_extraallocs.Grow() = alloc; memory -= chunk; taken += chunk; } else { chunk >>= 1; } } Warningf("Taken %" SIZETFMT "dKb of extra resource memory",taken >> 10); } void CFileMgr::FreeExtraResourceMem() { MEM_USE_USERDATA(MEMUSERDATA_TEXLITE); strStreamingAllocator& allocator = strStreamingEngine::GetAllocator(); for (int i = 0; i < s_extraallocs.GetCount();++i) { allocator.Free(s_extraallocs[i]); } s_extraallocs.Reset(); } #endif //!__FINAL static void MakePlatformPackName(char *outBuffer, size_t outBufferSize, const char *rawName, int packIndex) { if (gPlatformPackfileCount == 1) { Assert(packIndex == 0); safecpy(outBuffer, rawName, outBufferSize); } else { Assert(packIndex < gPlatformPackfileCount); const char *extension = strrchr(rawName, '.'); size_t baseSize = Min(outBufferSize-2, (size_t) (extension - rawName)); memcpy(outBuffer, rawName, baseSize); #if USE_NUMBERED_PLATPACKS formatf(&outBuffer[baseSize],outBufferSize-baseSize,"%02d",packIndex); safecat(outBuffer,extension,outBufferSize); #else outBuffer[baseSize] = 'a' + (char) packIndex; safecpy(&outBuffer[baseSize+1], extension, outBufferSize - baseSize - 1); #endif // USE_NUMBERED_PLATPACKS } } // callback for when blocking on loading to avoid network timeouts static const unsigned int KEEP_ALIVE_CALLBACK_INTERVAL = 50; static void KeepAliveCallback() { //CLoadingScreens::KeepAliveUpdate(); #if __BANK // Also keep the bank up to date here, to make sure we're flushing any queues (in particular the invoke queue) BANKMGR.Update(); #endif } void CFileMgr::PreSetupDevices() { // when running debug hdd builds on ng, mount all data on PC too. // this allows us to load rpfs from the pc if data there is newer than what's installed on console #if !__FINAL && (RSG_ORBIS || RSG_DURANGO) if ((!PARAM_forceboothdd.Get() && !PARAM_forceboothddloose.Get()) || PARAM_cleaninstall.Get()) return; const char* pRootFolder = ROOT_DIRECTORY; if (PARAM_rootdir.Get(pRootFolder)) fileAssertf((pRootFolder[strlen(pRootFolder)-1] == '/'), "-rootdir parameter requires terminating //!"); fiDevice::SetFindNewest(true); char commonFolder[MAX_DIRNAME_CHARS]; char platformFolder[MAX_DIRNAME_CHARS]; strcpy(commonFolder, COMMON_DIRECTORY); strcpy(platformFolder, PLATFORM_DIRECTORY); char pCommonFolderPath[RAGE_MAX_PATH] = {0}; char pPlatformFolderPath[RAGE_MAX_PATH] = {0}; formatf(pCommonFolderPath, "%s%s", pRootFolder, commonFolder); formatf(pPlatformFolderPath, "%s%s", pRootFolder, platformFolder); const char* pCommonFolder = pCommonFolderPath; const char* pPlatformFolder = pPlatformFolderPath; PARAM_common.Get(pCommonFolder); PARAM_platform.Get(pPlatformFolder); // setup "common:/" device gCommonDeviceRfs.Init(pCommonFolder, true); gCommonDeviceRfs.MountAs("common:/"); // mount crc device gCrcCommonDeviceRfs.Init("common:/", true, &gCommonDeviceRfs); gCrcCommonDeviceRfs.MountAs("commoncrc:/"); // setup "platform:/" device gPlatformDeviceRfs.Init(pPlatformFolder, true); gPlatformDeviceRfs.MountAs("platform:/"); // mount platformcrc device gCrcPlatformDeviceRfs.Init("platform:/", true, &gPlatformDeviceRfs); gCrcPlatformDeviceRfs.MountAs("platformcrc:/"); #endif // !__FINAL && (RSG_ORBIS || RSG_DURANGO) } char g_AudioPack[RAGE_MAX_PATH]; // // name: SetupDevices // description: void CFileMgr::SetupDevices() { PreSetupDevices(); char commonFolder[MAX_DIRNAME_CHARS]; char platformFolder[MAX_DIRNAME_CHARS]; char audioFolder[MAX_DIRNAME_CHARS]; strcpy(commonFolder, COMMON_DIRECTORY); strcpy(platformFolder, PLATFORM_DIRECTORY); if(PARAM_forceadpcm.Get()) { strcpy(audioFolder, "x64/audio/"); } else { strcpy(audioFolder, AUDIO_DIRECTORY); } fileDisplayf("Booting %s", sysParam::GetProgramName()); fiDeviceInstaller::SetKeepaliveCallback(datCallback(CFA(KeepAliveCallback)), KEEP_ALIVE_CALLBACK_INTERVAL); #if __CONSOLE #if RSG_ORBIS || RSG_DURANGO if ((fiDeviceInstaller::GetIsBootedFromHdd() || PARAM_playgoemu.Get()) && !PARAM_forceboothddloose.Get()) { if (PARAM_playgoemu.Get()) { PARAM_commonpack.Set(DISKROOT_DIRECTORY "common.rpf"); PARAM_platformpack.Set(DISKROOT_DIRECTORY PLATFORM_PACK); PARAM_audiopack.Set(DISKROOT_DIRECTORY AUDIO_DIRECTORY AUDIO_RPF); } else { PARAM_commonpack.Set(HDD_GAME_DIRECTORY_BASE "common.rpf"); PARAM_platformpack.Set(HDD_GAME_DIRECTORY_BASE PLATFORM_PACK); PARAM_audiopack.Set(HDD_GAME_DIRECTORY_BASE AUDIO_DIRECTORY AUDIO_RPF); } strCacheLoader::SetReadOnly(true); } else #endif if(sysBootManager::IsBootedFromDisc()) { const char* rootDir = GetRootFolder(); static char commonPath[RAGE_MAX_PATH]; static char platformPath[RAGE_MAX_PATH]; static char audioPath[RAGE_MAX_PATH]; if (gCommonRpfFromOdd) { formatf(commonPath, "%s%s", rootDir, "common.rpf"); } PARAM_commonpack.Set(commonPath); formatf(platformPath, "%s%s", rootDir, PLATFORM_PACK); PARAM_platformpack.Set(platformPath); formatf(audioPath, "%s" AUDIO_RPF, GetAudioFolder()); Displayf("Audio rpf = %s", audioPath); PARAM_audiopack.Set(audioPath); // #if !__FINAL // PARAM_noquits.Set("1"); // #endif strCacheLoader::SetReadOnly(true); } #if !__FINAL else if(PARAM_usepackfiles.Get()) { gPlatformPackfileCount = 1; // We never use split platform packfiles when running with -usepackfiles. PARAM_commonpack.Set("common.rpf"); PARAM_platformpack.Set(PLATFORM_PACK); strCacheLoader::SetReadOnly(true); } #endif #else // pc final builds assume packaged config - may switch this to __MASTER only #if __PACKEDDEBUG PARAM_commonpack.Set(DISKROOT_DIRECTORY "common.rpf"); PARAM_platformpack.Set(DISKROOT_DIRECTORY PLATFORM_PACK); PARAM_audiopack.Set(DISKROOT_DIRECTORY AUDIO_DIRECTORY AUDIO_RPF); #endif // !__FINAL #endif const char* pCommonFolder = commonFolder; const char* pPlatformFolder = platformFolder; const char* pCommonPack = NULL; const char* pPlatformPack = NULL; const char* pAudioFolder = audioFolder; #if RSG_ORBIS if(PARAM_forceadpcm.Get()) { formatf(g_AudioPack, "%sx64/audio/" AUDIO_RPF, GetRootFolder()); } else { formatf(g_AudioPack, "%sps4/audio/" AUDIO_RPF, GetRootFolder()); } #elif RSG_DURANGO formatf(g_AudioPack, "%sxboxone/audio/" AUDIO_RPF, GetRootFolder()); #elif RSG_PC formatf(g_AudioPack, "%sx64/audio/" AUDIO_RPF, GetRootFolder()); #else formatf(g_AudioPack, "%s" AUDIO_RPF, GetAudioFolder()); #endif Displayf("Audio rpf = %s", g_AudioPack); PARAM_common.Get(pCommonFolder); PARAM_platform.Get(pPlatformFolder); PARAM_audiofolder.Get(pAudioFolder); // Indicate whether or not this is a packaged build. #if !__PACKEDDEBUG sysBootManager::SetIsPackagedBuild(PARAM_platformpack.Get() && PARAM_commonpack.Get()); if (PARAM_commonpack.Get()) { // If we're grabbing common from a pack file, it's read only - that means we can't update the cache. // As such, the cache is expected to be there and up-to-date. PARAM_nocachetimestamps.Set(""); } #else // final can (and probably will) have packaged common data if (PARAM_commonpack.Get()) PARAM_nocachetimestamps.Set(""); #endif // !__FINAL // setup "common:/" device if(PARAM_commonpack.Get(pCommonPack)) { #if 0 CheckPack(gCommonPackfileDevice.Init(pCommonPack,true,fiPackfile::CACHE_MEMORY),pCommonPack); gCommonRpfInMemory = true; #else CheckPack(gCommonPackfileDevice.Init(pCommonPack,true,GetDefaultPackfileCacheMode()),pCommonPack); #endif gCommonPackfileDevice.MountAs("common:/"); gCommonDevice.Init(pCommonFolder, true); gCommonDevice.MountAs("common:/"); // mount crc device gCrcCommonDevice.Init("common:/", true, &gCommonPackfileDevice); gCrcCommonDevice.MountAs("commoncrc:/"); } else { gCommonDevice.Init(pCommonFolder, true NOTFINAL_ONLY(&& sysBootManager::IsPackagedBuild())); // A packaged build might overlap common:/ with a read-only device, and we can't have mismatching readonly flags gCommonDevice.MountAs("common:/"); // mount crc device gCrcCommonDevice.Init("common:/", true, &gCommonDevice); gCrcCommonDevice.MountAs("commoncrc:/"); } // Let's also set up the cache folder. This can be different in some build types where common:/ is read-only. // We WILL have to point it to the read-only RPF in some cases - like in disc builds. // Since common:/ can be read-only, we have to use a separate mapping here. bool packedCommon = pCommonPack!=NULL; if (fiDeviceInstaller::GetIsBootedFromHdd() || PARAM_playgoemu.Get()) { packedCommon = true; #if RSG_ORBIS || RSG_DURANGO if (PARAM_forceboothddloose.Get()) { packedCommon = false; } #endif } // setup "platform:/" device if(PARAM_platformpack.Get(pPlatformPack)) { gPlatformDevice.Init(pPlatformFolder, true); gPlatformDevice.MountAs("platform:/"); // mount platformcrc device gCrcPlatformDevice.Init("platform:/", true, &gPlatformDevice); gCrcPlatformDevice.MountAs("platformcrc:/"); if (CFileMgr::IsGameInstalled()) { for (int x=0; xInit(packPath,true,GetDefaultPackfileCacheMode()), packPath); formatf(packPath, "audio:/sfx/%s/", line); pack->MountAs(packPath); } CloseFile(handle); } else { audErrorf("Failed to open audio packlist: %s", packList); } } else { // MEGA RPF SUPPORT static const char* const audioPackfiles[] = { "audio_cs", "audio_misc", "audio_music", "audio_radio" }; for(int i = 0; i < NELEM(audioPackfiles); i++) { char packPath[RAGE_MAX_PATH]; formatf(packPath, "%ssfx/%s.rpf", GetAudioFolder(), audioPackfiles[i]); fiPackfile *pack = rage_new fiPackfile(); gAudioPacks.Grow() = pack; CheckPack(pack->Init(packPath, true, GetDefaultPackfileCacheMode()), packPath); pack->MountAs("audio:/sfx/"); } if(!secondaryInit) { fiDeviceRelative *occlusionDevice = rage_new fiDeviceRelative(); occlusionDevice->Init("audio:/"); #if RSG_ORBIS occlusionDevice->MountAs("ps4/audio/"); #elif RSG_DURANGO occlusionDevice->MountAs("xboxone/audio/"); #elif RSG_PC occlusionDevice->MountAs("x64/audio/"); #else occlusionDevice->MountAs("platform:/audio/"); #endif } } } // // name: CFileMgr::SetupDevicesAfterInit // description: shutdown memory version of packfile void CFileMgr::SetupDevicesAfterInit() { const char* pCommonPack = NULL; if(PARAM_commonpack.Get(pCommonPack) && gCommonRpfInMemory) { fiDevice::Unmount("common:/"); // shutdown memory device and init file device gCommonPackfileDevice.Shutdown(); gCommonPackfileDevice.Init(pCommonPack,true,GetDefaultPackfileCacheMode()); gCommonPackfileDevice.MountAs("common:/"); } } void CFileMgr::Initialise() { ms_allChunksOnHdd = StreamingInstall::Init(); #if !__FINAL // pretend we need to run the streaming install code for debugging and such if (PARAM_playgotest.Get()) { ms_allChunksOnHdd = false; } #endif #if RSG_DURANGO || (RSG_ORBIS && !__FINAL) #if !__FINAL if (PARAM_forceboothdd.Get() || PARAM_forceboothddloose.Get()) #endif #if RSG_DURANGO || !__FINAL { SetGameHddBootPath(HDD_GAME_DIRECTORY_BASE); } #endif #endif // RSG_DURANGO || RSG_ORBIS #if RSG_ORBIS && !__FINAL if (PARAM_playgoemu.Get()) { SetGameHddBootPath(DISKROOT_DIRECTORY); } #endif strcpy(ms_rootDirName, GetRootFolder()); #if RSG_DURANGO && !__FINAL if (PARAM_forceboothdd.Get() || PARAM_forceboothddloose.Get()) { // Add the build branch as a secondary directory so we can open files from there strncat_s(ms_rootDirName, ";", _TRUNCATE); strncat_s(ms_rootDirName, ROOT_DIRECTORY, _TRUNCATE); } #endif // RSG_DURANGO && !__FINAL ASSET.SetPath(ms_rootDirName); fiDevice::InitClass(); fiDevice::sm_FatalReadError = &ReadErrorHandler; PF_START_STARTUPBAR("Init image list"); strPackfileManager::InitImageList(); #if __WIN32PC // These need to be passed in once we know where they'll come from ms_appPath = "\\Rockstar Games\\GTA V\\"; ms_userPath = "\\Rockstar Games\\GTA V\\"; ms_rgscPath = "\\Rockstar Games\\RGSC\\"; // get the current working dir on systems that support it. //_getcwd(ms_rootDirName, MAX_DIRNAME_CHARS); char dir[MAX_DIRNAME_CHARS]; char path[MAX_DIRNAME_CHARS]; //grab the directory and the path from the exe if (!_splitpath_s(sysParam::sm_ProgName, dir, MAX_DIRNAME_CHARS, path, MAX_DIRNAME_CHARS, NULL, 0, NULL, 0)) { safecat(dir, path, MAX_DIRNAME_CHARS); safecpy(ms_rootDirName, dir, MAX_DIRNAME_CHARS); //fix up any `/` in the path size_t length = strlen(ms_rootDirName); for (size_t i=0;i/Local Settings/Application Data/ ms_originalAppDataRootDirName = WideToUtf8(TempPathBuffer, reinterpret_cast(AppDataRoot), MAX_DIRNAME_CHARS, MAX_DIRNAME_CHARS, &UnusedOut); ms_originalAppDataRootDirName += ms_appPath; ms_appDataRootDirName = ms_originalAppDataRootDirName; ms_rockStarRootDirName = TempPathBuffer; ms_rockStarRootDirName += ms_rgscPath; } else { Assert(0); } wchar_t UserDataRoot[MAX_DIRNAME_CHARS] = {0}; if ( SUCCEEDED( SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, UserDataRoot) ) ) { // CSIDL_PERSONAL = My Documents ms_originalUserDataRootDirName = WideToUtf8(TempPathBuffer, reinterpret_cast(UserDataRoot), MAX_DIRNAME_CHARS, MAX_DIRNAME_CHARS, &UnusedOut);; ms_originalUserDataRootDirName += ms_userPath; ms_userDataRootDirName = ms_originalUserDataRootDirName; } else { Assert(0); } #endif PF_START_STARTUPBAR("Setup devices"); //@@: location CFILEMGR_INITIALISE SetupDevices(); fileAssertf(!strstr(ms_rootDirName,"VS_Project"),"Your working directory is highly likely to be wrong. Currently is %s", ms_rootDirName); // DW - fed up of the working directory being wrong and it crashing later in the code. } // end - CFileMgr::Initialise // // name: NeedsPath // description: Returns if a filename needs the root directory prepended to it bool CFileMgr::NeedsPath(const char *base) { if ( !base ) return true; // If the name starts with either slash, or contains a colon anywhere, it does not need a path. if (base[0] == '/' || base[0] == '\\' || strchr(base,':')) return false; else return true; } #if RSG_DURANGO || RSG_ORBIS void CFileMgr::SetGameHddBootPath(const char* hddBootPath) { strncpy(ms_hddBootPath, hddBootPath, MAX_DIRNAME_CHARS); fiDeviceInstaller::SetIsBootedFromHdd(true, ms_hddBootPath); } #endif // RSG_DURANGO || RSG_ORBIS void CFileMgr::SetDir(const char* pDirName) { #if __WIN32PC sysCriticalSection oLock(s_FileMgrToken); #endif ms_dirName[0] ='\0'; if(NeedsPath(pDirName)) strcpy(ms_dirName, ms_rootDirName); if(pDirName[0] != '\0') { strcat(ms_dirName, pDirName); if(pDirName[strlen(pDirName)-1] != '\\') strcat(ms_dirName, "\\"); } ASSET.SetPath(ms_dirName); } // end - CFileMgr::SetDir #if __WIN32PC void CFileMgr::SetAppDataDir(const char* pDirName, bool bUseOriginal) { sysCriticalSection oLock(s_FileMgrToken); ms_dirName[0] = '\0'; atFixedString dirToSet; if(NeedsPath(pDirName)) { if( bUseOriginal ) dirToSet = ms_originalAppDataRootDirName; else dirToSet = ms_appDataRootDirName; } SetPCDir(dirToSet, pDirName); } // end - CFileMgr::SetAppDataDir void CFileMgr::SetUserDataDir(const char* pDirName, bool bUseOriginal) { sysCriticalSection oLock(s_FileMgrToken); ms_dirName[0] = '\0'; atFixedString dirToSet; if(NeedsPath(pDirName)) { if( bUseOriginal ) dirToSet = ms_originalUserDataRootDirName; else dirToSet = ms_userDataRootDirName; } SetPCDir(dirToSet, pDirName); } // end - CFileMgr::SetUserDataDir void CFileMgr::SetRockStarDir(const char* pDirName) { sysCriticalSection oLock(s_FileMgrToken); ms_dirName[0] = '\0'; atFixedString dirToSet = ms_rockStarRootDirName; SetPCDir(dirToSet, pDirName); } // end - CFileMgr::SetRockStartDataDir void CFileMgr::SetPCDir(atFixedString& baseDir, const char* subDir) { if(subDir[0] != '\0') { baseDir += subDir; if(!baseDir.EndsWith("\\") && !baseDir.EndsWith("/")) baseDir += "/"; safecpy(ms_dirName, baseDir.c_str(), sizeof(ms_dirName)); USES_CONVERSION; int result = SHCreateDirectoryExW(NULL, reinterpret_cast(UTF8_TO_WIDE(ms_dirName)), NULL); if(result != ERROR_SUCCESS && result != ERROR_ALREADY_EXISTS) { Assertf(0, "Can't create directory: %s \n", ms_dirName);} } ASSET.SetPath(ms_dirName); } #endif // __WIN32PC // Name : LoadFile // Purpose : Loads a file into the specified buffer // Parameters : pFilename - file to load // pBuffer - ptr to buffer to load file into // bufferSize - maximum size of file to copy to buffer // if 0, no action is taken // Returns : File size or -1 if there is an error s32 CFileMgr::LoadFile(const char* pFilename, u8* pBuffer, s32 DEV_ONLY(bufferSize), const char* UNUSED_PARAM(pReadString)) { fiStream* pStream; s32 size; #if __DEV fileDebugf1("LoadFile: %s Size:%d", pFilename, bufferSize); #endif // open file pStream = ASSET.Open(pFilename, ""); if(pStream == NULL) return -1; size = pStream->Size(); pStream->Read(pBuffer, size); // place end of file character at end of buffer pBuffer[size] = '\0'; // close file pStream->Close(); #if __DEV fileDebugf1("Done LoadFile: %s Size:%d", pFilename, size); #endif return size; } // end - CFileMgr::LoadFile // Name : OpenFile // Purpose : opens a file and returns a file id // Parameters : pFilename - file to load FileHandle CFileMgr::OpenFile(const char* pFilename, const char* pReadString) { fiStream* pStream; bool bReadonly = true; bool bCreate = false; // check if 'w' is in readstring if(pReadString) { while(*pReadString) { if(*pReadString == 'a') bReadonly = false; if(*pReadString == 'w') bCreate = true; pReadString++; } } fileDebugf1("Opening %s", pFilename); if(bCreate) { pStream = ASSET.Create(pFilename, "", false); } else { pStream = ASSET.Open(pFilename, "", false, bReadonly); } return (FileHandle)pStream; } // Name : OpenFileFroWriting // Purpose : opens a file and returns a file id // Parameters : pFilename - file to load FileHandle CFileMgr::OpenFileForWriting(const char* pFilename) { return OpenFile(pFilename, "w"); } FileHandle CFileMgr::OpenFileForAppending(const char* pFilename) { FileHandle NewFileHandle = OpenFile(pFilename, "a"); if(NewFileHandle!=INVALID_FILE_HANDLE) { fiStream* pStream = (fiStream*) NewFileHandle; pStream->Seek(pStream->Size()); } return NewFileHandle; } s32 CFileMgr::Read(FileHandle id, char* pData, s32 size) { fiStream* pStream = (fiStream*)id; return pStream->Read(pData, size); } s32 CFileMgr::Write(FileHandle id, const char* pData, s32 size) { fiStream* pStream = (fiStream*)id; return pStream->Write(pData, size); } s32 CFileMgr::Seek(FileHandle id, s32 offset) { fiStream* pStream = (fiStream*)id; return pStream->Seek(offset); } s32 CFileMgr::Flush(FileHandle id) { fiStream* pStream = (fiStream*)id; return pStream->Flush(); } // // name: ReadLine // description: Read a single line from the file bool CFileMgr::ReadLine(FileHandle id, char* pLine, s32 maxLen) { fiStream* pStream = (fiStream*)id; s32 read = fgets(pLine,maxLen-1,pStream); if (read == 0 && pStream->Tell() >= pStream->Size()) return false; s32 i; for(i=0; i= 0) || *pC == ',')) *pC = ' '; pC++; } } // get beginning of line first non-space/non-control character while(*pLineStart == ' ' && *pLineStart != '\0') pLineStart++; return pLineStart; } return NULL; } // // name: CloseFile // description: Close a file handle s32 CFileMgr::CloseFile(FileHandle id) { fiStream* pStream = (fiStream*)id; return pStream->Close(); } // // name: GetTotalSize // description: Get size of file s32 CFileMgr::GetTotalSize(FileHandle id) { fiStream* pStream = (fiStream*)id; return pStream->Size(); } s32 CFileMgr::Tell(FileHandle id) { fiStream* pStream = (fiStream*)id; return pStream->Tell(); } bool CFileMgr::HasAsyncInstallFinished() { if (IsGameInstalled()) return true; return StreamingInstall::HasInstallFinished(); } void CFileMgr::CleanupAsyncInstall() { if (!HasAsyncInstallFinished()) return; StreamingInstall::Shutdown(); if (!IsGameInstalled()) { ms_allChunksOnHdd = true; const char* pPlatformPack = NULL; if (PARAM_platformpack.Get(pPlatformPack)) { for (int x=PLAYGO_PACK_COUNT; x