Code archives/Miscellaneous/Save settings to correct location on Windows

This code has been declared by its author to be Public Domain code.

Download source code

Save settings to correct location on Windows by BlitzSupport2014
This system allows you to save your application's preferences in the correct location on Windows Vista, 7/8 and upwards, whereas trying to save into your application's own folder may fail due to UAC settings/other filesystem behaviours on these OSes.

Just amend ApplicationName and PrefsFilename globals to suit, then call LoadSettings (), SaveSettings () and GetPrefsFile ().

Try the demo! Recommend reading the Usage notes first...

For real-world usage, you'll have to modify LoadSettings and WriteSettingsFile to accommodate your game's variables/structure (eg. your variables may be in a class/Type rather than globals).
' ----------------------------------------------------------
' Reads and writes save-files in correct location on Windows!
' ----------------------------------------------------------

Rem

	Usage:
	
	1)	Modify the ApplicationName and PrefsFilename globals to suit
	your application. Can be left as defaults for demo.
	
	2)	Use only the three functions below:

	LoadSettings ()	-	loads your settings file, if it exists

	SaveSettings ()	-	creates folder in correct location (if needed);
						creates prefs file in folder (if needed);
						saves prefs!

	GetPrefsFile ()	-	returns path of prefs file, based on
						ApplicationName and PrefsFilename.

						Modify these two globals below to suit yourself!

						Note on application name/prefs filename:

						To avoid conflict with other applications,
						probably best to use your developer/studio
						name for ApplicationName (this is the folder
						name) and the name of your game for
						PrefsFilename (eg. "Rocket Raiders.cfg")

	3)	On running the demo, navigate to the folder name returned by GetPrefsFile ()
		to see the folder/prefs file. Delete the folder to clean up after playing
		around! Try re-running several times, and try deleting prefs file/folder
		and re-running...
		
End Rem

' ----------------------------------------------------------
' N O T E S . . .
' ----------------------------------------------------------

' Just call SaveSettings to set up your save folder/file for the first time.

' You'll have to amend LoadSettings to accommodate your game's variables and/or structure.

' Amend WriteSettingsFile to suit your game. (SaveSettings is just a wrapper around this.)

SuperStrict

?Win32 ' Windows only!

Global ApplicationName:String = "AAA Example Game"
Global PrefsFilename:String = "prefs.txt"

' Some example variables to be saved/loaded:

Global Example_Lives:Int = 0
Global Example_Level:String = ""

' Uses an example save file of the form:

Rem

	LIVES:Integer
	LEVEL:String
	
	... eg.
	
	LIVES:5
	LEVEL:Space

End Rem

' Windows special path constants:

Const CSIDL_PERSONAL:Int = $5 ' Use this instead of CSIDL_MYDOCUMENTS. I don't know why! Ask Microsoft...
Const CSIDL_APPDATA:Int = $1A

' From http://www.blitzbasic.com/codearcs/codearcs.php?code=2815
' See URL for more special folder locations!

Function GetSpecialFolder:String (folder:Int)

	' Shell32 functions...
	
	Global SHGetSpecialFolderLocation_	(hwndOwner:Byte Ptr, nFolder:Int, pidl:Byte Ptr) "win32"
	Global SHGetPathFromIDList_			(pidl:Byte Ptr, bytearray:Byte Ptr) "win32"
	
	' OLE32 functions...
	
	Global CoTaskMemFree_ (pv:Byte Ptr)
	
	' Assign function pointers...
	
	Local shell32:Int	= LoadLibraryA ("shell32.dll")
	Local ole32:Int		= LoadLibraryA ("ole32.dll")

	Local result:Int = False
	
	If shell32

		SHGetSpecialFolderLocation_	= GetProcAddress (shell32, "SHGetSpecialFolderLocation")
		SHGetPathFromIDList_		= GetProcAddress (shell32, "SHGetPathFromIDList")

		If (Not SHGetSpecialFolderLocation_) Or (Not SHGetPathFromIDList_)
			DebugLog "Failed to assign shell32 function pointer!"
			Return ""
		EndIf

	Else

		DebugLog "Failed to open shell32.dll!"
		Return ""

	EndIf

	If ole32

		CoTaskMemFree_ = GetProcAddress (ole32, "CoTaskMemFree")

		If Not CoTaskMemFree_
			DebugLog "Failed to assign ole32 function pointer!"
			Return ""
		EndIf

	Else

		DebugLog "Failed to open ole32.dll!"
		Return ""

	EndIf

	Function GetSpecialFolder_Sub:String(folder_id:Int) ' JoshK's code brutally hacked-in!

		Local idl:TBank = CreateBank (8) 
		Local pathbank:TBank = CreateBank (260) 
		Local n%
		Local sp$
		Local b:Int

		If SHGetSpecialFolderLocation_ (Null, folder_id, BankBuf (idl)) = 0		

			SHGetPathFromIDList_ Byte Ptr PeekInt (idl, 0), BankBuf (pathbank)

			For n = 0 To 259
				b = PeekByte (pathbank, n)
				If b = 0
					CoTaskMemFree_ (Byte Ptr PeekInt (idl, 0))
					Return sp
				EndIf
				sp$ = sp$ + Chr (b)
			Next
		Else
			Return ""
		EndIf
		
		CoTaskMemFree_ (Byte Ptr PeekInt (idl, 0))
		
		Return sp.Trim ()
		
	End Function

	If SHGetSpecialFolderLocation_ And SHGetPathFromIDList_ And CoTaskMemFree_
		Return GetSpecialFolder_Sub (folder)
	EndIf

End Function

' Helper function...

Function Quoted:String (in:String)
	Return "~q" + in + "~q"
End Function

' Retrieves location of prefs file based on ApplicationName and PrefsFilename,
' whether or not they've been created yet...

Function GetPrefsFile:String ()

	Local folder:String = GetSpecialFolder (CSIDL_APPDATA)

	If folder = ""

		folder = GetSpecialFolder (CSIDL_PERSONAL)

		If folder = ""
			folder = "C:" ' Gah! What's up with your system?!
		EndIf

	EndIf
	
	If Right (folder, 1) <> "\" And Right (folder, 1) <> "/"
		folder = folder + "\"
	EndIf
	
	Return folder + ApplicationName + "\" + PrefsFilename
	
End Function

' ----------------------------------------------------------
' Deletes settings file...
' ----------------------------------------------------------

Function DeleteSettings ()
	DeleteFile (GetPrefsFile ())
End Function

' ----------------------------------------------------------
' Loads settings, if settings file exists...
' ----------------------------------------------------------

' Use SaveSettings to set up prefs folder/file!

Function LoadSettings ()

	Local loadprefs:TStream = ReadFile (GetPrefsFile ())
	
	If loadprefs
	
		While Not Eof (loadprefs)
			
			Local info:String = ReadLine (loadprefs)

			' Example line:
			
			' LEVEL:Space
			
			Local splitter:Int = Instr (info, ":")
			
			Local entry:String = Trim (Left (info, splitter - 1))
			Local value:String = Trim (Mid (info, splitter + 1))
			
			' Amend the Case entries and Example_Variables to suit your game!
			
			Select entry

				Case "LIVES"
				
					' Read an Int...
					
					Example_Lives = Int (value)

				Case "LEVEL"
				
					' Read a String...
					
					Example_Level = value

				Default

			EndSelect
			
		Wend

		CloseFile loadprefs

	Else
		Print "Couldn't load preferences file! Does " + Quoted (GetPrefsFile ()) + " exist?"
	EndIf

End Function

' ----------------------------------------------------------
' Save settings. Modify the highlighted section to suit!
' ----------------------------------------------------------

Function WriteSettingsFile (folder:String)

	If Right (folder, 1) = "\" Or Right (folder, 1) = "/"
		folder = folder [..Len (folder) - 1] ' FileType doesn't like end slashes on folders!
	EndIf
	
	Local savefile:String = folder + "\" + PrefsFilename

	If FileType (folder) = FILETYPE_DIR
	
		Local created:Int = CreateFile (savefile)
		
		If created
		
			Local saveprefs:TStream = WriteFile (savefile)
			
			If saveprefs

				' ----------------------------------------------------------
				' *** MODIFY ME! ***
				' ----------------------------------------------------------
		
				' ----------------------------------------------------------
				' The settings save section - amend to suit your game!
				' ----------------------------------------------------------

				WriteLine saveprefs, "LIVES:" + Example_Lives
				WriteLine saveprefs, "LEVEL:" + Example_Level

				' ----------------------------------------------------------
				' *** END OF MODIFY ME! ***
				' ----------------------------------------------------------

				CloseFile saveprefs

			Else

				Print "Unable to create preferences file " + Quoted (savefile)
			EndIf

		EndIf
	
	Else
		Print "Unable to locate preferences folder " + Quoted (folder)
		Print "Folder is " + FileType (folder)
		Print "Folder is " + FileType ("C:\Users\James\AppData\Roaming\AAA Example Game\")
		Print "Folder is " + FileType ("C:\Users\James\AppData\Roaming\AAA Example Game")
	EndIf

End Function

' ----------------------------------------------------------
' Wrapper for WriteSettingsFile...
' ----------------------------------------------------------

Function SaveSettings ()
	
	Local folder:String = ExtractDir (GetPrefsFile ())
	
	folder = Replace (folder, "/", "\") ' WTF, ExtractDir?!

	CreateDir folder$

	Select FileType (folder$)
	
		Case 0
		
			Print "Unable to create preferences folder " + Quoted (folder)
			
		Case FILETYPE_DIR
		
			WriteSettingsFile folder
			
		Case FILETYPE_FILE ' Unlikely!
		
			Print "Unable to create preferences folder + " + Quoted (folder) + "; file with same name already exists!"
			
	EndSelect
	
End Function

? ' End of ?Win32 section!

' ----------------------------------------------------------
' D E M O . . .
' ----------------------------------------------------------

' Some random level names for saving...

Local Level_Name:String [10]

Level_Name [0] = "Snow World"
Level_Name [1] = "Eiffel Tower"
Level_Name [2] = "Egypt"
Level_Name [3] = "Space"
Level_Name [4] = "Moon"
Level_Name [5] = "Hell"
Level_Name [6] = "Desert"
Level_Name [7] = "Circus"
Level_Name [8] = "Inca"
Level_Name [9] = "Alien Planet"

SeedRnd MilliSecs ()

Print ""
Print "Using " + GetPrefsFile () ' Just for info

' ----------------------------------------------------------
' Load settings... expected to fail if not already saved!
' ----------------------------------------------------------

Print ""
Print "Attempting to load settings..."
Print ""

LoadSettings

' ----------------------------------------------------------
' Show loaded settings... will be default null on first run
' ----------------------------------------------------------

Print ""
Print "Loaded settings:"
Print ""
Print "~t~tLives: " + Example_Lives
Print "~t~tLevel: " + Example_Level
Print ""

' ----------------------------------------------------------
' Randomly change settings...
' ----------------------------------------------------------

Print ""
Print "Randomly changing settings..."
Print ""

Example_Lives = Rand (1, 10)
Example_Level = Level_Name [Rand (0, 9)]

' ----------------------------------------------------------
' Save settings...
' ----------------------------------------------------------

Print ""
Print "Saving new settings..."
Print ""

SaveSettings

' ----------------------------------------------------------
' Reload saved settings...
' ----------------------------------------------------------

Print ""
Print "Attempting to reload saved settings..."
Print ""

LoadSettings

' ----------------------------------------------------------
' Show loaded settings...
' ----------------------------------------------------------

Print ""
Print "Loaded settings:"
Print ""
Print "~t~tLives: " + Example_Lives
Print "~t~tLevel: " + Example_Level
Print ""

Comments

None.

Code Archives Forum