@@ -34,7 +34,7 @@ struct LauncherProduct {
3434 let gameDisplayName : String
3535 let steamGameDirectoryName : String
3636 let steamGameIDs : [ String ]
37- let gogFallbackRelativePath : String
37+ let gogInstallRegistryKey : String
3838 let steamUserRelativePath : String
3939 let wineAppDefaultExeName : String
4040 let wineDLLOverrides : String
@@ -751,6 +751,52 @@ final class LauncherEngine {
751751 return nil
752752 }
753753
754+ private func readRegistryValue( keyPath: String , valueName: String ) -> String ? {
755+ let task = Process ( )
756+ task. executableURL = paths. wineBin
757+ task. arguments = [ " reg " , " query " , keyPath, " /v " , valueName]
758+
759+ let outputPipe = Pipe ( )
760+ task. standardOutput = outputPipe
761+ task. standardError = outputPipe
762+
763+ do {
764+ try task. run ( )
765+ task. waitUntilExit ( )
766+ } catch {
767+ log ( " Failed to query registry key \( keyPath) : \( error. localizedDescription) " )
768+ return nil
769+ }
770+
771+ guard task. terminationStatus == 0 else {
772+ log ( " Registry key not found or unreadable: \( keyPath) [value= \( valueName) ] " )
773+ return nil
774+ }
775+
776+ let outputData = outputPipe. fileHandleForReading. readDataToEndOfFile ( )
777+ guard let output = String ( data: outputData, encoding: . utf8) else {
778+ log ( " Failed to decode registry output for key: \( keyPath) " )
779+ return nil
780+ }
781+
782+ let escapedValueName = NSRegularExpression . escapedPattern ( for: valueName)
783+ let pattern = " ^ \\ s* \( escapedValueName) \\ s+REG_ \\ w+ \\ s+(.+?) \\ s*$ "
784+ guard let regex = try ? NSRegularExpression ( pattern: pattern, options: [ . anchorsMatchLines] ) else {
785+ log ( " Failed to compile registry parsing regex for value: \( valueName) " )
786+ return nil
787+ }
788+
789+ let range = NSRange ( output. startIndex... , in: output)
790+ guard let match = regex. firstMatch ( in: output, options: [ ] , range: range) ,
791+ let valueRange = Range ( match. range ( at: 1 ) , in: output) else {
792+ log ( " Registry value \( valueName) not found in key: \( keyPath) " )
793+ return nil
794+ }
795+
796+ let value = String ( output [ valueRange] ) . trimmingCharacters ( in: . whitespacesAndNewlines)
797+ return value. isEmpty ? nil : value
798+ }
799+
754800 private func findSteamGamePath( ) -> GameInstallLocation ? {
755801 let steamConfig = FileManager . default. homeDirectoryForCurrentUser
756802 . appendingPathComponent ( " Library/Application Support/Steam/steamapps/libraryfolders.vdf " )
@@ -854,18 +900,29 @@ final class LauncherEngine {
854900 }
855901
856902 private func findGOGGamePath( ) -> GameInstallLocation ? {
857- let gogPath = paths. winePrefix. appendingPathComponent ( product. gogFallbackRelativePath) . path
903+ let registryKey = product. gogInstallRegistryKey. trimmingCharacters ( in: . whitespacesAndNewlines)
904+ guard !registryKey. isEmpty else {
905+ return nil
906+ }
907+
908+ guard let windowsPath = readRegistryValue ( keyPath: registryKey, valueName: " path " ) else {
909+ return nil
910+ }
911+
912+ let gogURL = convertWindowsPathToPrefixPath ( windowsPath)
913+ let gogPath = gogURL. path
858914
859915 if FileManager . default. fileExists ( atPath: gogPath) {
860- log ( " Found \( product. gameDisplayName) at GOG fallback : \( gogPath) " )
916+ log ( " Found \( product. gameDisplayName) via GOG registry key : \( gogPath) " )
861917 return GameInstallLocation (
862918 path: gogPath,
863- installDir: URL ( fileURLWithPath : gogPath ) . lastPathComponent,
919+ installDir: gogURL . lastPathComponent,
864920 gameID: " " ,
865921 libraryPath: " "
866922 )
867923 }
868924
925+ log ( " GOG registry path does not exist in prefix: \( gogPath) " )
869926 return nil
870927 }
871928
@@ -1120,7 +1177,7 @@ final class LauncherEngine {
11201177 let presentAlert = {
11211178 let alert = NSAlert ( )
11221179 alert. messageText = " Path not found "
1123- alert. informativeText = " Could not locate \( self . product. gameDisplayName) in Steam paths or GOG fallback path . "
1180+ alert. informativeText = " Could not locate \( self . product. gameDisplayName) in Steam paths or GOG location . "
11241181 alert. alertStyle = . warning
11251182 alert. addButton ( withTitle: " OK " )
11261183 NSApp . activate ( ignoringOtherApps: true )
@@ -1201,11 +1258,12 @@ final class LauncherEngine {
12011258 showStatusMessage ( " Locating game path... " , style: . informational)
12021259
12031260 if product. id == " memoria " {
1261+ let gogInPrefixPath = findGOGGamePath ( ) . map { URL ( fileURLWithPath: $0. path) }
12041262 let winePrefixCandidates : [ URL ] = [
12051263 paths. gameDirectory,
12061264 paths. targetExe. deletingLastPathComponent ( ) ,
12071265 paths. winePrefix. appendingPathComponent ( " drive_c/Program Files (x86)/Steam/steamapps/common/ \( product. steamGameDirectoryName) " ) ,
1208- paths . winePrefix . appendingPathComponent ( product . gogFallbackRelativePath )
1266+ gogInPrefixPath
12091267 ] . compactMap { $0 }
12101268
12111269 guard let currentGamePath = winePrefixCandidates. first ( where: {
0 commit comments