66from collections .abc import Iterable
77from dataclasses import dataclass
88from pathlib import Path
9- from typing import Literal , TypeVar
9+ from typing import Any , Literal , TypeVar
1010
1111import mobase
1212from PyQt6 .QtCore import QDateTime , QDir , qCritical , qInfo , qWarning
@@ -199,6 +199,7 @@ class Cyberpunk2077Game(BasicGame):
199199
200200 # CET and RED4ext, relative to Cyberpunk2077.exe
201201 _forced_libraries = ["version.dll" , "winmm.dll" ]
202+ _crashreporter_path = "bin/x64/CrashReporter/CrashReporter.exe"
202203
203204 _redmod_binary = Path ("tools/redmod/bin/redMod.exe" )
204205 _redmod_log = Path ("tools/redmod/bin/REDmodLog.txt" )
@@ -232,6 +233,8 @@ def init(self, organizer: mobase.IOrganizer) -> bool:
232233 )
233234 organizer .onAboutToRun (self ._onAboutToRun )
234235 organizer .onPluginSettingChanged (self ._on_settings_changed )
236+ organizer .modList ().onModInstalled (self ._check_disable_crashreporter )
237+ organizer .onUserInterfaceInitialized (self ._check_disable_crashreporter )
235238 return True
236239
237240 def _on_settings_changed (
@@ -247,6 +250,73 @@ def _on_settings_changed(
247250 elif setting == "reverse_remod_load_order" :
248251 self ._modlist_files ["redmod" ].reversed_priority = bool (new )
249252
253+ def _check_disable_crashreporter (
254+ self , mod : mobase .IModInterface | None | Any = None
255+ ):
256+ """
257+ Disable Crashreporter with CET installed in VFS, since it crashes
258+ when trying to resolve `version.dll`.
259+ """
260+ if not self ._get_setting ("disable_crashreporter" ):
261+ return
262+ cet_mod_name = self ._find_cet_mod_name (
263+ mod if isinstance (mod , mobase .IModInterface ) else None
264+ )
265+ if (
266+ cet_mod_name
267+ and (cr_origin := self ._organizer .getFileOrigins (self ._crashreporter_path ))
268+ and cr_origin [0 ] == "data"
269+ ):
270+ self ._create_dummy_crashreporter_mod (
271+ self ._organizer .modList ().priority (cet_mod_name ) + 1
272+ )
273+
274+ def _find_cet_mod_name (self , mod : mobase .IModInterface | None = None ) -> str :
275+ """
276+ Find the mod containing `version.dll`.
277+
278+ Args:
279+ mod (optional): check the mods filetree instead. Defaults to None.
280+
281+ Returns:
282+ The mods name if `version.dll` is found, else ''.
283+ """
284+ cet_mod_name = ""
285+ if mod :
286+ if mod .fileTree ().find ("bin/x64/version.dll" ):
287+ cet_mod_name = mod .name ()
288+ elif dll_origins := self ._organizer .getFileOrigins ("bin/x64/version.dll" ):
289+ cet_mod_name = dll_origins [0 ]
290+ return cet_mod_name if cet_mod_name != "data" else ""
291+
292+ def _create_dummy_crashreporter_mod (self , priority : int = 0 ):
293+ """
294+ Disables CrashReporter by creating an empty dummy file to replace
295+ `CrashReporter.exe`.
296+ """
297+ mod_name = "disable CrashReporter (MO CET fix)"
298+ modlist = self ._organizer .modList ()
299+ if modlist .getMod (mod_name ):
300+ return
301+ qInfo (f"CET VFS fix: creating mod { mod_name } " )
302+ new_mod = self ._organizer .createMod (mobase .GuessedString (mod_name ))
303+ if not new_mod :
304+ return
305+ mod_name = new_mod .name ()
306+ new_mod .setGameName (self .gameShortName ())
307+ new_mod .setUrl (f"{ self .getSupportURL ()} #crashreporter" )
308+ crashReporter = Path (new_mod .absolutePath (), self ._crashreporter_path )
309+ crashReporter .parent .mkdir (parents = True )
310+ crashReporter .touch ()
311+
312+ def callback ():
313+ modlist .setActive (mod_name , True )
314+ modlist .setPriority (mod_name , priority )
315+
316+ self ._organizer .onNextRefresh (callback , False )
317+ self ._organizer .refresh ()
318+ self ._set_setting ("disable_crashreporter" , False )
319+
250320 def iniFiles (self ):
251321 return ["UserSettings.json" ]
252322
@@ -310,6 +380,14 @@ def settings(self) -> list[mobase.PluginSetting]:
310380 ),
311381 True ,
312382 ),
383+ mobase .PluginSetting (
384+ "disable_crashreporter" ,
385+ (
386+ "Creates a dummy mod/file to disable CrashReporter.exe, "
387+ "which is not compatible with VFS version.dll, see wiki"
388+ ),
389+ True ,
390+ ),
313391 ]
314392
315393 def _get_setting (self , key : str ) -> mobase .MoVariant :
0 commit comments