6
6
import sys
7
7
import pprint
8
8
import json
9
+ import re
9
10
10
11
import sgtk
11
12
from sgtk .platform import SoftwareLauncher , SoftwareVersion , LaunchInformation
@@ -148,6 +149,7 @@ def _find_software(self):
148
149
Find installed UE executable.
149
150
150
151
:returns: List of :class:`SoftwareVersion` instances.
152
+ :raises RuntimeError: For unsupported OSes.
151
153
"""
152
154
self .logger .info ("Finding Unreal Engine executables" )
153
155
sw_versions = []
@@ -161,35 +163,43 @@ def _find_software(self):
161
163
elif sgtk .util .is_linux ():
162
164
executable_templates = self .EXECUTABLE_TEMPLATES .get ("linux" )
163
165
164
- if executable_templates :
165
- for executable_template in executable_templates :
166
- self .logger .debug ("Processing template %s." , executable_template )
167
- executable_matches = self ._glob_and_match (
168
- executable_template , self .COMPONENT_REGEX_LOOKUP
169
- )
170
- # Extract all products from that executable.
171
- for (executable_path , key_dict ) in executable_matches :
172
- # extract the matched keys form the key_dict (default to None if
173
- # not included)
174
- executable_version = key_dict .get ("version" )
175
- details = self ._get_unreal_version_details (executable_path )
176
- if details and all (x in details for x in ["MajorVersion" , "MinorVersion" , "PatchVersion" ]):
177
- executable_version = "%s.%s.%s" % (
178
- details ["MajorVersion" ],
179
- details ["MinorVersion" ],
180
- details ["PatchVersion" ],
181
- )
182
- sw_versions .append (
183
- SoftwareVersion (
184
- executable_version ,
185
- "Unreal Engine" ,
186
- executable_path ,
187
- os .path .join (self .disk_location , "icon_256.png" ),
188
- )
189
- )
190
- else :
166
+ if not executable_templates :
191
167
raise RuntimeError ("Unsupported platform %s" % sys .platform )
192
168
169
+ for executable_template in executable_templates :
170
+ self .logger .debug ("Processing template %s." , executable_template )
171
+ executable_matches = self ._glob_and_match (
172
+ executable_template , self .COMPONENT_REGEX_LOOKUP
173
+ )
174
+ # Extract all products from that executable.
175
+ for (executable_path , key_dict ) in executable_matches :
176
+ # extract the matched keys form the key_dict (default to None if
177
+ # not included)
178
+ executable_version = key_dict .get ("version" )
179
+ details = self ._get_unreal_version_details (executable_path )
180
+ if details and all (x in details for x in ["MajorVersion" , "MinorVersion" , "PatchVersion" ]):
181
+ executable_version = "%s.%s.%s" % (
182
+ details ["MajorVersion" ],
183
+ details ["MinorVersion" ],
184
+ details ["PatchVersion" ],
185
+ )
186
+ sw_versions .append (
187
+ SoftwareVersion (
188
+ executable_version ,
189
+ "Unreal Engine" ,
190
+ executable_path ,
191
+ os .path .join (self .disk_location , "icon_256.png" ),
192
+ )
193
+ )
194
+ if sgtk .util .is_windows ():
195
+ # On Windows we also explore registry, but make sure to not add
196
+ # things twice.
197
+ found_paths = [sw .path for sw in sw_versions ]
198
+ for sw in self ._find_software_from_registry ():
199
+ if sw .path not in found_paths :
200
+ sw_versions .append (sw )
201
+ found_paths .append (sw .path )
202
+
193
203
return sw_versions
194
204
195
205
def _get_unreal_version_details (self , executable_path ):
@@ -208,3 +218,118 @@ def _get_unreal_version_details(self, executable_path):
208
218
with open (full_path ) as pf :
209
219
version_details = json .load (pf )
210
220
return version_details
221
+
222
+ def _get_unreal_version (self , executable_path ):
223
+ """
224
+ Return the Unreal Editor version for the given executable.
225
+
226
+ :returns: A potentially empty version string.
227
+ """
228
+ details = self ._get_unreal_version_details (executable_path )
229
+ if details and all (x in details for x in ["MajorVersion" , "MinorVersion" , "PatchVersion" ]):
230
+ return "%s.%s.%s" % (
231
+ details ["MajorVersion" ],
232
+ details ["MinorVersion" ],
233
+ details ["PatchVersion" ],
234
+ )
235
+ # Fall back on parsing the executable path
236
+ # Look for ue_4.7, ue_4.7.2, etc..
237
+ for part in executable_path .lower ().split (os .path .sep ):
238
+ m = re .match (r"ue_([0-9]+(?:\.[0-9]+)*)$" , part )
239
+ if m :
240
+ return m .group (1 )
241
+ return ""
242
+
243
+ def _get_win_executable_path (self , install_path ):
244
+ """
245
+ Return the Unreal Editor exe path for the given install path, if one
246
+ exists.
247
+
248
+ Check if the executable exists on the filesystem.
249
+ :returns: A string, full path to the Unreal editor executable or ``None``.
250
+ """
251
+ binary_path = os .path .normpath (
252
+ os .path .join (install_path , "Engine" , "Binaries" , "Win64" )
253
+ )
254
+ exec_path = os .path .join (binary_path , "UE4Editor.exe" )
255
+ if os .path .exists (exec_path ):
256
+ self .logger .info ("Found %s" % exec_path )
257
+ return exec_path
258
+ # From UE5, the exe name changed
259
+ exec_path = os .path .join (binary_path , "UnrealEditor.exe" )
260
+ if os .path .exists (exec_path ):
261
+ self .logger .info ("Found %s" % exec_path )
262
+ return exec_path
263
+
264
+ self .logger .info (
265
+ "Couldn't find executable in installation path %s" % install_path
266
+ )
267
+ return None
268
+
269
+ def _find_software_from_registry (self ):
270
+ """
271
+ Find executables in the Windows Registry.
272
+
273
+ :returns: List of :class:`SoftwareVersion` instances.
274
+ """
275
+ self .logger .info ("Detecting Unreal Engine from registry..." )
276
+ try :
277
+ # Note: keeping this as is, without knowing why it was implemented
278
+ # like that, instead of just doing import winreg
279
+ import _winreg
280
+ except ImportError :
281
+ import winreg as _winreg
282
+
283
+ self .logger .debug (
284
+ "Querying windows registry for key HKEY_LOCAL_MACHINE\\ SOFTWARE\\ EpicGames\\ Unreal Engine"
285
+ )
286
+ base_key_name = "SOFTWARE\\ EpicGames\\ Unreal Engine"
287
+ # find all subkeys in key HKEY_LOCAL_MACHINE\SOFTWARE\EpicGames\Unreal Engine
288
+ sw_versions = []
289
+ try :
290
+ key = _winreg .OpenKey (_winreg .HKEY_LOCAL_MACHINE , base_key_name )
291
+ sub_key_count = _winreg .QueryInfoKey (key )[0 ]
292
+ for i in range (sub_key_count ):
293
+ sub_key_name = _winreg .EnumKey (key , i )
294
+ sub_key = _winreg .OpenKey (key , sub_key_name )
295
+ values_count = _winreg .QueryInfoKey (sub_key )[1 ]
296
+ if sub_key_name == "Builds" :
297
+ for j in range (values_count ):
298
+ value = _winreg .EnumValue (sub_key , j )[1 ]
299
+ self .logger .info ("Checking %s" % value )
300
+ if value and os .path .exists (value ):
301
+ executable_path = self ._get_win_executable_path (value )
302
+ if executable_path :
303
+ sw = SoftwareVersion (
304
+ self ._get_unreal_version (executable_path ),
305
+ "Unreal Engine (Dev Build)" ,
306
+ executable_path ,
307
+ os .path .join (self .disk_location , "icon_256.png" )
308
+ )
309
+ sw_versions .append (sw )
310
+ else :
311
+ for j in range (values_count ):
312
+ value_name , value , _ = _winreg .EnumValue (sub_key , j )
313
+ if value_name == "InstalledDirectory" :
314
+ if value and os .path .exists (value ):
315
+ executable_path = self ._get_win_executable_path (value )
316
+ if executable_path :
317
+ sw = SoftwareVersion (
318
+ self ._get_unreal_version (executable_path ),
319
+ "Unreal Engine" ,
320
+ executable_path ,
321
+ os .path .join (self .disk_location , "icon_256.png" )
322
+ )
323
+ sw_versions .append (sw )
324
+
325
+ break
326
+ _winreg .CloseKey (key )
327
+ except WindowsError as e :
328
+ self .logger .error ("Error handling key %s: %s" % (base_key_name , e ))
329
+ # Log the traceback in debug
330
+ self .logger .debug (
331
+ "Error opening key %s: %s" % (base_key_name , e ),
332
+ exc_info = True
333
+ )
334
+
335
+ return sw_versions
0 commit comments