@@ -640,6 +640,174 @@ const Source1AppidInfo_t *GetKnownAppidInfo( uint32 nAppid )
640
640
return nullptr ;
641
641
}
642
642
643
+ #ifdef BDSBASE
644
+ #ifndef ENGINE_DLL
645
+ // ---------------------------------------------------------------------------------------------
646
+ // Purpose: This function gets the Steam installation path. Calls for platform specific things.
647
+ // ---------------------------------------------------------------------------------------------
648
+ static const char *GetSteamInstallationPath ()
649
+ {
650
+ // Steam path to pass over
651
+ static char szSteamPath[1024 ] = {};
652
+ #ifdef WIN32
653
+ // Open the registry to look for the Steam installation.
654
+ HKEY hKey = nullptr ;
655
+ LSTATUS status = RegOpenKeyEx (
656
+ HKEY_CURRENT_USER,
657
+ TEXT ( " SOFTWARE\\ Valve\\ Steam" ),
658
+ 0 ,
659
+ KEY_READ,
660
+ &hKey );
661
+
662
+ // Check result
663
+ if ( status != ERROR_SUCCESS )
664
+ return nullptr ;
665
+
666
+ // Query the SteamPath key
667
+ char szPathBuf[1024 ] = {};
668
+ DWORD dwBufSize = sizeof ( szPathBuf );
669
+ DWORD dwType = 0 ;
670
+ LSTATUS result = RegQueryValueEx (
671
+ hKey,
672
+ " SteamPath" ,
673
+ NULL ,
674
+ &dwType,
675
+ reinterpret_cast <BYTE*>( szPathBuf ),
676
+ &dwBufSize );
677
+
678
+ // check for the result
679
+ if ( result != ERROR_SUCCESS )
680
+ return nullptr ;
681
+
682
+ // Close the registry key, we're done with it
683
+ RegCloseKey ( hKey );
684
+
685
+ // Put this in the steam path var.
686
+ Q_strncpy ( szSteamPath, szPathBuf, sizeof ( szSteamPath ) );
687
+ #else
688
+ // No registry on Linux, look for the symlink.
689
+ const char *pszHomeDir = getenv ( " HOME" );
690
+ const char *pszSteamPath = " /.steam/steam" ;
691
+ Q_snprintf ( szSteamPath, sizeof ( szSteamPath ), " %s%s" , pszHomeDir, pszSteamPath );
692
+ #endif // WIN32 ELSE !WIN32
693
+ return szSteamPath;
694
+ }
695
+
696
+ // -----------------------------------------------------------------------------
697
+ // Purpose: Function for mod based projects to find installed game directories
698
+ // Since non-engine mods don't have access to SteamApps, they can use this.
699
+ // This goes through three steps:
700
+ // 1. Finds the main Steam installation in GetSteamInstallationPath (queries registry on Windows, checks through symlink on others)
701
+ // 2. Parse the libraryfolders.vdf file in Steam's config folder to find the appID
702
+ // 3. Find the appmanifest_appID.acf file and look for the name. If so, return the folder name
703
+ // Note: Slashes are fixed right before it's returned.
704
+ // Input: game appID
705
+ // Output: returns the install directory IF it's installed, otherwise returns nullptr
706
+ // -----------------------------------------------------------------------------
707
+ static const char *GetAppInstallDirNoSteam ( int nAppID )
708
+ {
709
+ // First, get the Steam installation.
710
+ char szSteamPath[1024 ] = {};
711
+ Q_strncpy ( szSteamPath, GetSteamInstallationPath (), sizeof ( szSteamPath ) );
712
+ if ( !szSteamPath || szSteamPath[0 ] == ' \0 ' )
713
+ return nullptr ;
714
+
715
+ // Second, go to the libraryfolders.vdf and look if the appid is in them.
716
+ char szLibraryFoldersFile[1024 ] = {};
717
+ Q_snprintf ( szLibraryFoldersFile, sizeof ( szLibraryFoldersFile ), " %s%s" , szSteamPath, " /config/libraryfolders.vdf" );
718
+
719
+ // NOTE: ReadKeyValuesFile already deletes the KV if it doesn't exist
720
+ // read the libraryfolders.vdf file
721
+ KeyValues *pRootKV = ReadKeyValuesFile ( szLibraryFoldersFile );
722
+ if ( !pRootKV )
723
+ return nullptr ;
724
+
725
+ // Get a string representation of the Steam AppID passed
726
+ char szAppID[32 ] = {};
727
+ Q_snprintf ( szAppID, sizeof ( szAppID ), " %d" , nAppID );
728
+
729
+ // Path KV
730
+ KeyValues *pPathKV = nullptr ;
731
+
732
+ // Go thru each possible install path until we find the app.
733
+ FOR_EACH_TRUE_SUBKEY ( pRootKV, pKVPath )
734
+ {
735
+ // Check for the apps subkey
736
+ KeyValues *pApps = pKVPath->FindKey ( " apps" );
737
+ if ( !pApps )
738
+ continue ;
739
+
740
+ // Try to find the appid path
741
+ if ( pApps->FindKey ( szAppID ) )
742
+ {
743
+ // Okay, we found the app id key. now use the path key from the root KV
744
+ pPathKV = pKVPath->FindKey ( " path" ); // This is null checked below.
745
+ break ;
746
+ }
747
+ }
748
+
749
+ // Error out if we can't find it
750
+ if ( !pPathKV )
751
+ return nullptr ;
752
+
753
+ // Look for the game that this mod asked for
754
+ char szInstallationPath[1024 ] = {};
755
+ // copy this string over since we're gonna delete the KV
756
+ Q_strncpy ( szInstallationPath, pPathKV->GetString (), sizeof ( szInstallationPath ) );
757
+ // If it's empty somehow, error out
758
+ if ( !szInstallationPath || szInstallationPath[0 ] == ' \0 ' )
759
+ {
760
+ // Clear this keyvalue.
761
+ pRootKV->deleteThis ();
762
+ pRootKV = nullptr ;
763
+ return nullptr ;
764
+ }
765
+
766
+ // we no longer need this KV
767
+ pRootKV->deleteThis ();
768
+ pRootKV = nullptr ;
769
+
770
+ // get the appmanifest_APPID.acf file
771
+ char szAppManifestPath[1024 ] = {};
772
+ Q_snprintf ( szAppManifestPath, sizeof ( szAppManifestPath ), " %s%s%s.acf" , szInstallationPath, " /steamapps/appmanifest_" , szAppID );
773
+ // Reuse the root pointer to open the appmanifest file
774
+ pRootKV = ReadKeyValuesFile ( szAppManifestPath );
775
+
776
+ // Error out if this file can't be found
777
+ if ( !pRootKV )
778
+ return nullptr ;
779
+
780
+ // Find the install dir
781
+ KeyValues *pInstallDirKV = pRootKV->FindKey ( " installdir" );
782
+
783
+ // Error out if we can't find this key
784
+ if ( !pInstallDirKV )
785
+ return nullptr ;
786
+
787
+ // Get the game name to get it from the common dir.
788
+ // Again, deleting the KV after this
789
+ char szGameName[256 ] = {};
790
+ Q_strncpy ( szGameName, pInstallDirKV->GetString (), sizeof ( szGameName ) );
791
+
792
+ if ( !szGameName || szGameName[0 ] == ' \0 ' )
793
+ return nullptr ;
794
+
795
+ // Not needed anymore
796
+ pRootKV->deleteThis ();
797
+ pRootKV = nullptr ;
798
+
799
+ // Now that we got everything, put it all together
800
+ static char szAbsoluteGameDirPath[1024 ] = {};
801
+ Q_snprintf ( szAbsoluteGameDirPath, sizeof ( szAbsoluteGameDirPath ), " %s%s%s" , szInstallationPath, " /common/" , szGameName );
802
+
803
+ // Fix the slashes before passing it
804
+ V_FixDoubleSlashes ( szAbsoluteGameDirPath );
805
+ V_FixSlashes ( szAbsoluteGameDirPath );
806
+ return szAbsoluteGameDirPath;
807
+ }
808
+ #endif // ENGINE_DLL
809
+ #endif
810
+
643
811
FSReturnCode_t FileSystem_LoadSearchPaths ( CFSSearchPathsInit &initInfo )
644
812
{
645
813
if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName )
@@ -687,15 +855,86 @@ FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
687
855
const char *pszPathID = pCur->GetName ();
688
856
const char *pLocation = pCur->GetString ();
689
857
const char *pszBaseDir = baseDir;
858
+ #ifndef BDSBASE
690
859
#ifdef ENGINE_DLL
691
860
char szAppInstallDir[ 1024 ];
861
+ #endif
692
862
#endif
693
863
694
864
if ( !FileSystem_AllowedSearchPath ( pLocation ) )
695
865
continue ;
696
866
697
867
if ( Q_stristr ( pLocation, APPID_PREFIX_TOKEN ) == pLocation )
698
868
{
869
+ #ifdef BDSBASE
870
+ Location += strlen ( APPID_PREFIX_TOKEN );
871
+ const char *pNumberLoc = pLocation;
872
+ int nAppId = V_atoi ( pNumberLoc );
873
+ pLocation = Q_stristr ( pLocation, " |" );
874
+ if ( !pLocation )
875
+ {
876
+ Error ( " Malformed gameinfo.txt" );
877
+ }
878
+ pLocation += strlen ( " |" );
879
+
880
+ if ( !nAppId )
881
+ {
882
+ Error ( " Can't mount content from invalid appid." );
883
+ }
884
+
885
+ // Get the known appid info
886
+ const Source1AppidInfo_t *pKnownAppid = GetKnownAppidInfo ( nAppId );
887
+ const char *pszAppName = pKnownAppid ? pKnownAppid->pszName : " Unknown" ;
888
+
889
+ // Store the app install directory here.
890
+ char szAppInstallDir[1024 ] = {};
891
+ #ifdef ENGINE_DLL
892
+ if ( !SteamApps () )
893
+ {
894
+ Error ( " No SteamApps connection." );
895
+ }
896
+
897
+ if ( !SteamApps ()->BIsSubscribedApp ( nAppId ) )
898
+ {
899
+ char szStoreCommand[4096 ];
900
+ V_sprintf_safe ( szStoreCommand, " steam://store/%d" , nAppId );
901
+ Plat_OpenURL ( szStoreCommand );
902
+
903
+ Error ( " This mod requires that you own %s (%d). Please purchase it, and install it to play this mod." , pszAppName, nAppId );
904
+ }
905
+
906
+ if ( !SteamApps ()->BIsAppInstalled ( nAppId ) )
907
+ {
908
+ char szInstallCommand[4096 ];
909
+ V_sprintf_safe ( szInstallCommand, " steam://install/%d" , nAppId );
910
+ Plat_OpenURL ( szInstallCommand );
911
+
912
+ Error ( " This mod requires %s (%d) to be installed. Please install it to play this mod." , pszAppName, nAppId );
913
+ }
914
+
915
+ uint32 unLength = SteamApps ()->GetAppInstallDir ( nAppId, szAppInstallDir, sizeof ( szAppInstallDir ) );
916
+ if ( !unLength )
917
+ {
918
+ Error ( " Couldn't get install dir for appid: %d" , nAppId );
919
+ }
920
+ #else
921
+ // Get the install directory.
922
+ const char *pszAppDir = GetAppInstallDirNoSteam ( nAppId );
923
+ if ( !pszAppDir || pszAppDir[0 ] == ' \0 ' )
924
+ {
925
+ // Tell them to install this mod, but we don't have SteamApps here, so let them see the
926
+ // "no licenses error" if they don't own this game.
927
+ char szInstallCommand[4096 ] = {};
928
+ V_sprintf_safe ( szInstallCommand, " steam://install/%d" , nAppId );
929
+ Plat_OpenURL ( szInstallCommand );
930
+ Error ( " This mod requires %s (%d) to be installed. Please install it to play this mod." , pszAppName, nAppId );
931
+ }
932
+
933
+ // Copy the result over to szAppInstallDir.
934
+ V_strncpy ( szAppInstallDir, pszAppDir, sizeof ( szAppInstallDir ) );
935
+ #endif // ENGINE_DLL ELSE !ENGINE_DLL
936
+ pszBaseDir = szAppInstallDir;
937
+ #else
699
938
#ifdef ENGINE_DLL
700
939
pLocation += strlen ( APPID_PREFIX_TOKEN );
701
940
const char *pNumberLoc = pLocation;
@@ -747,6 +986,7 @@ FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
747
986
pszBaseDir = szAppInstallDir;
748
987
#else
749
988
Error ( " Appid based mounting is not supported on non-engine DLL projects." );
989
+ #endif
750
990
#endif
751
991
}
752
992
else if ( Q_stristr ( pLocation, GAMEINFOPATH_TOKEN ) == pLocation )
0 commit comments