@@ -2715,31 +2715,189 @@ static void COM_Game_f (void)
27152715
27162716/*
27172717=================
2718- COM_IsValidBaseDir
2718+ COM_IsValidFlavorDir
27192719
2720- Returns true if the directory contains usable game data:
2721- either classic id1/pak0.pak or the rerelease QuakeEX.kpf
2720+ Returns true if the directory contains usable game data for the given
2721+ flavor: classic id1/pak0.pak or the rerelease QuakeEX.kpf (-1 accepts
2722+ either)
27222723=================
27232724*/
2724- static qboolean COM_IsValidBaseDir (const char * dir )
2725+ static qboolean COM_IsValidFlavorDir (const char * dir , int flavor )
27252726{
27262727 char path [MAX_OSPATH ];
27272728
2728- if ((size_t )q_snprintf (path , sizeof (path ), "%s/" GAMENAME "/pak0.pak" , dir ) < sizeof (path ) && Sys_FileType (path ) == FS_ENT_FILE )
2729+ if (flavor != QUAKE_FLAVOR_REMASTERED && (size_t )q_snprintf (path , sizeof (path ), "%s/" GAMENAME "/pak0.pak" , dir ) < sizeof (path ) &&
2730+ Sys_FileType (path ) == FS_ENT_FILE )
27292731 return true;
2730- if ((size_t )q_snprintf (path , sizeof (path ), "%s/QuakeEX.kpf" , dir ) < sizeof (path ) && Sys_FileType (path ) == FS_ENT_FILE )
2732+ if (flavor != QUAKE_FLAVOR_ORIGINAL && (size_t )q_snprintf (path , sizeof (path ), "%s/QuakeEX.kpf" , dir ) < sizeof (path ) &&
2733+ Sys_FileType (path ) == FS_ENT_FILE )
27312734 return true;
27322735
27332736 return false;
27342737}
27352738
2739+ static qboolean COM_IsValidBaseDir (const char * dir )
2740+ {
2741+ return COM_IsValidFlavorDir (dir , -1 );
2742+ }
2743+
2744+ /*
2745+ =================
2746+ COM_RequestedQuakeFlavor
2747+
2748+ Quake version requested on the command line, -1 if none
2749+ =================
2750+ */
2751+ static int COM_RequestedQuakeFlavor (void )
2752+ {
2753+ if (COM_CheckParm ("-prefremaster" ) || COM_CheckParm ("-remaster" ) || COM_CheckParm ("-remastered" ))
2754+ return QUAKE_FLAVOR_REMASTERED ;
2755+ if (COM_CheckParm ("-preforiginal" ) || COM_CheckParm ("-original" ))
2756+ return QUAKE_FLAVOR_ORIGINAL ;
2757+ return -1 ;
2758+ }
2759+
2760+ #ifdef USE_SDL3
2761+ /*
2762+ =================
2763+ COM_LoadSelectedBaseDirs / COM_SaveSelectedBaseDirs
2764+
2765+ Game folders the user picked in the folder dialog, kept in the pref
2766+ dir. A new pick is only written once the engine is fully initialized
2767+ (COM_WriteSelectedBaseDir) so a folder with broken data can't get
2768+ remembered.
2769+ =================
2770+ */
2771+ static int com_pendingbasedirflavor = -1 ;
2772+ static char com_pendingbasedir [MAX_OSPATH ];
2773+
2774+ static void COM_LoadSelectedBaseDirs (char * original , size_t originalsize , char * remastered , size_t remasteredsize )
2775+ {
2776+ char line [MAX_OSPATH + 16 ];
2777+ FILE * f ;
2778+ char * pref_path = SDL_GetPrefPath ("" , "vkQuake" );
2779+
2780+ f = fopen (va ("%s/basedirs.txt" , pref_path ), "r" );
2781+ SDL_free (pref_path );
2782+ if (!f )
2783+ return ;
2784+
2785+ while (fgets (line , sizeof (line ), f ))
2786+ {
2787+ char * path = strchr (line , ' ' );
2788+ if (!path )
2789+ continue ;
2790+ * path ++ = '\0' ;
2791+ path [strcspn (path , "\r\n" )] = '\0' ;
2792+ if (!strcmp (line , "classic" ))
2793+ q_strlcpy (original , path , originalsize );
2794+ else if (!strcmp (line , "remastered" ))
2795+ q_strlcpy (remastered , path , remasteredsize );
2796+ }
2797+
2798+ fclose (f );
2799+ }
2800+
2801+ static void COM_SaveSelectedBaseDirs (const char * original , const char * remastered )
2802+ {
2803+ FILE * f ;
2804+ char * pref_path = SDL_GetPrefPath ("" , "vkQuake" );
2805+
2806+ f = fopen (va ("%s/basedirs.txt" , pref_path ), "w" );
2807+ SDL_free (pref_path );
2808+ if (!f )
2809+ return ;
2810+
2811+ if (original [0 ])
2812+ fprintf (f , "classic %s\n" , original );
2813+ if (remastered [0 ])
2814+ fprintf (f , "remastered %s\n" , remastered );
2815+
2816+ fclose (f );
2817+ }
2818+
2819+ /*
2820+ =================
2821+ COM_SelectBaseDir
2822+
2823+ Asks the user for a game folder until it contains data for the wanted
2824+ flavor (-1 accepts either); returns false if the dialog was cancelled
2825+ =================
2826+ */
2827+ static qboolean COM_SelectBaseDir (int flavor , const char * default_location , char * dst , size_t dstsize )
2828+ {
2829+ const char * title , * complaint ;
2830+
2831+ switch (flavor )
2832+ {
2833+ case QUAKE_FLAVOR_ORIGINAL :
2834+ title = "Select your classic Quake folder" ;
2835+ complaint = "The selected folder does not contain " GAMENAME "/pak0.pak." ;
2836+ break ;
2837+ case QUAKE_FLAVOR_REMASTERED :
2838+ title = "Select your remastered Quake folder" ;
2839+ complaint = "The selected folder does not contain QuakeEX.kpf." ;
2840+ break ;
2841+ default :
2842+ title = "Select your Quake folder" ;
2843+ complaint = "The selected folder does not contain Quake game data (" GAMENAME "/pak0.pak or QuakeEX.kpf)." ;
2844+ break ;
2845+ }
2846+
2847+ while (Sys_SelectFolder (title , default_location , dst , dstsize ))
2848+ {
2849+ if (COM_IsValidFlavorDir (dst , flavor ))
2850+ return true;
2851+ SDL_ShowSimpleMessageBox (SDL_MESSAGEBOX_WARNING , "vkQuake" , complaint , NULL );
2852+ }
2853+
2854+ return false;
2855+ }
2856+
2857+ static void COM_SetPendingBaseDir (int flavor , const char * dir )
2858+ {
2859+ com_pendingbasedirflavor = flavor ;
2860+ q_strlcpy (com_pendingbasedir , dir , sizeof (com_pendingbasedir ));
2861+ }
2862+ #endif
2863+
2864+ /*
2865+ =================
2866+ COM_WriteSelectedBaseDir
2867+
2868+ Remembers the folder picked in the dialog; called once the engine is
2869+ fully initialized as proof the folder contains working game data
2870+ =================
2871+ */
2872+ void COM_WriteSelectedBaseDir (void )
2873+ {
2874+ #ifdef USE_SDL3
2875+ char original [MAX_OSPATH ] = {0 };
2876+ char remastered [MAX_OSPATH ] = {0 };
2877+
2878+ if (com_pendingbasedirflavor < 0 )
2879+ return ;
2880+
2881+ COM_LoadSelectedBaseDirs (original , sizeof (original ), remastered , sizeof (remastered ));
2882+ if (com_pendingbasedirflavor == QUAKE_FLAVOR_REMASTERED )
2883+ q_strlcpy (remastered , com_pendingbasedir , sizeof (remastered ));
2884+ else
2885+ q_strlcpy (original , com_pendingbasedir , sizeof (original ));
2886+ COM_SaveSelectedBaseDirs (original , remastered );
2887+
2888+ com_pendingbasedirflavor = -1 ;
2889+ #endif
2890+ }
2891+
27362892/*
27372893=================
27382894COM_FindStoreBaseDir
27392895
27402896Locates a Steam/GOG/Epic Games Store install of Quake and points
27412897com_basedir at it (based on the Ironwail startup flow). Used when
27422898the working directory has no game data and no -basedir was given.
2899+ Asks the user for the folder when the requested version isn't found,
2900+ starting at the previously picked folder.
27432901=================
27442902*/
27452903static void COM_FindStoreBaseDir (void )
@@ -2748,6 +2906,7 @@ static void COM_FindStoreBaseDir (void)
27482906 char original [MAX_OSPATH ] = {0 };
27492907 char remastered [MAX_OSPATH ] = {0 };
27502908 quakeflavor_t flavor ;
2909+ int requested ;
27512910 qboolean force_steam = COM_CheckParm ("-steam" ) != 0 ;
27522911 qboolean force_gog = COM_CheckParm ("-gog" ) != 0 ;
27532912 qboolean force_egs = (COM_CheckParm ("-egs" ) || COM_CheckParm ("-epic" )) != 0 ;
@@ -2776,11 +2935,77 @@ static void COM_FindStoreBaseDir (void)
27762935 remastered [0 ] = '\0' ;
27772936 }
27782937
2779- if (original [0 ] && !COM_IsValidBaseDir (original ))
2938+ if (original [0 ] && !COM_IsValidFlavorDir (original , QUAKE_FLAVOR_ORIGINAL ))
27802939 original [0 ] = '\0' ;
2781- if (remastered [0 ] && !COM_IsValidBaseDir (remastered ))
2940+ if (remastered [0 ] && !COM_IsValidFlavorDir (remastered , QUAKE_FLAVOR_REMASTERED ))
27822941 remastered [0 ] = '\0' ;
27832942
2943+ requested = COM_RequestedQuakeFlavor ();
2944+
2945+ if (!forced && !isDedicated )
2946+ {
2947+ #ifdef USE_SDL3
2948+ char stored_original [MAX_OSPATH ] = {0 };
2949+ char stored_remastered [MAX_OSPATH ] = {0 };
2950+
2951+ COM_LoadSelectedBaseDirs (stored_original , sizeof (stored_original ), stored_remastered , sizeof (stored_remastered ));
2952+
2953+ // use the folder picked in a previous run unless the user wants a new one
2954+ if (!COM_CheckParm ("-select-basedir" ))
2955+ {
2956+ if (!original [0 ] && stored_original [0 ] && COM_IsValidFlavorDir (stored_original , QUAKE_FLAVOR_ORIGINAL ))
2957+ q_strlcpy (original , stored_original , sizeof (original ));
2958+ if (!remastered [0 ] && stored_remastered [0 ] && COM_IsValidFlavorDir (stored_remastered , QUAKE_FLAVOR_REMASTERED ))
2959+ q_strlcpy (remastered , stored_remastered , sizeof (remastered ));
2960+ }
2961+
2962+ // still missing: ask for the folder, starting the dialog at the previous pick
2963+ if (requested == QUAKE_FLAVOR_ORIGINAL && !original [0 ])
2964+ {
2965+ if (!COM_SelectBaseDir (QUAKE_FLAVOR_ORIGINAL , stored_original , original , sizeof (original )))
2966+ {
2967+ SDL_Quit ();
2968+ exit (0 );
2969+ }
2970+ COM_SetPendingBaseDir (QUAKE_FLAVOR_ORIGINAL , original );
2971+ }
2972+ else if (requested == QUAKE_FLAVOR_REMASTERED && !remastered [0 ])
2973+ {
2974+ if (!COM_SelectBaseDir (QUAKE_FLAVOR_REMASTERED , stored_remastered , remastered , sizeof (remastered )))
2975+ {
2976+ SDL_Quit ();
2977+ exit (0 );
2978+ }
2979+ COM_SetPendingBaseDir (QUAKE_FLAVOR_REMASTERED , remastered );
2980+ }
2981+ else if (requested < 0 && !original [0 ] && !remastered [0 ])
2982+ {
2983+ char selected [MAX_OSPATH ];
2984+ if (!COM_SelectBaseDir (-1 , stored_remastered [0 ] ? stored_remastered : stored_original , selected , sizeof (selected )))
2985+ {
2986+ SDL_Quit ();
2987+ exit (0 );
2988+ }
2989+ if (COM_IsValidFlavorDir (selected , QUAKE_FLAVOR_REMASTERED ))
2990+ {
2991+ q_strlcpy (remastered , selected , sizeof (remastered ));
2992+ COM_SetPendingBaseDir (QUAKE_FLAVOR_REMASTERED , selected );
2993+ }
2994+ else
2995+ {
2996+ q_strlcpy (original , selected , sizeof (original ));
2997+ COM_SetPendingBaseDir (QUAKE_FLAVOR_ORIGINAL , selected );
2998+ }
2999+ }
3000+ #else
3001+ // no folder picker without the SDL3 dialog API
3002+ if (requested == QUAKE_FLAVOR_ORIGINAL && !original [0 ])
3003+ Sys_Error ("Couldn't find the classic Quake folder. Use -basedir to specify it." );
3004+ else if (requested == QUAKE_FLAVOR_REMASTERED && !remastered [0 ])
3005+ Sys_Error ("Couldn't find the remastered Quake folder. Use -basedir to specify it." );
3006+ #endif
3007+ }
3008+
27843009 if (!original [0 ] && !remastered [0 ])
27853010 {
27863011 if (force_steam )
@@ -2792,15 +3017,12 @@ static void COM_FindStoreBaseDir (void)
27923017 return ; // fall through to the regular missing-data error
27933018 }
27943019
2795- if (original [0 ] && remastered [0 ])
2796- {
2797- if (COM_CheckParm ("-prefremaster" ) || COM_CheckParm ("-remaster" ) || COM_CheckParm ("-remastered" ))
2798- flavor = QUAKE_FLAVOR_REMASTERED ;
2799- else if (COM_CheckParm ("-preforiginal" ) || COM_CheckParm ("-original" ))
2800- flavor = QUAKE_FLAVOR_ORIGINAL ;
2801- else
2802- flavor = ChooseQuakeFlavor ();
2803- }
3020+ if (requested == QUAKE_FLAVOR_REMASTERED && remastered [0 ])
3021+ flavor = QUAKE_FLAVOR_REMASTERED ;
3022+ else if (requested == QUAKE_FLAVOR_ORIGINAL && original [0 ])
3023+ flavor = QUAKE_FLAVOR_ORIGINAL ;
3024+ else if (original [0 ] && remastered [0 ])
3025+ flavor = ChooseQuakeFlavor ();
28043026 else
28053027 flavor = remastered [0 ] ? QUAKE_FLAVOR_REMASTERED : QUAKE_FLAVOR_ORIGINAL ;
28063028
0 commit comments