From a553a72758ec3d46a12c7943585d930383f0043e Mon Sep 17 00:00:00 2001 From: "Herman S." Date: Sat, 27 Jul 2024 13:44:27 +0200 Subject: [PATCH 1/5] Add functions to implement Wine detection --- Monika After Story/game/definitions.rpy | 119 ++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/Monika After Story/game/definitions.rpy b/Monika After Story/game/definitions.rpy index 8a431a471b..e7c3ed29e3 100644 --- a/Monika After Story/game/definitions.rpy +++ b/Monika After Story/game/definitions.rpy @@ -7947,3 +7947,122 @@ init python: and should_kiss and mas_timePastSince(persistent._mas_last_kiss, cooldown) ) + + def mas_RegOpenKeyEx(hive, subkey, ul_options=0x0, sam_desired=0x20019): + """ + Opens Windows registry key and returns its HKEY handle value. + + CONDITIONS: + Unavailable on non-Windows platforms (raises NotImplementedError.) + + IN: + hive: + Windows API constant value for registry hive + subkey: + Registry key path (e.g. Software\Windows) + ul_options: + See Windows API documentation. + 0x0 by default. + sam_desired: + Key open mode (read/write), see Windows API documentation. + 0x20019 to open key for reading by default. + + OUT: + ctypes.wintypes.HKEY: + Handle HKEY for the opened registry key. + + RAISES: + NotImplementedError: + If called on non-Windows platform. + OSError: + If an error has occurred calling Windows API function. + """ + + if not renpy.windows: + raise NotImplementedError("RegOpenKeyEx is only supported on Windows.") + + import ctypes + from ctypes import wintypes + + AdvApi32 = ctypes.WinDLL("advapi32") + + RegOpenKeyEx = AdvApi32.RegOpenKeyExW + RegOpenKeyEx.argtypes = [ + wintypes.HKEY, # hKey + wintypes.LPCWSTR, # lpSubKey + wintypes.DWORD, # ulOptions + wintypes.REGSAM, # samDesired (i.e. open mode, read/write, see WinAPI docs) + ctypes.POINTER(wintypes.HKEY) # phkResult (handle to use for reading etc.) + ] + RegOpenKeyEx.restype = wintypes.LONG + + res_hkey = wintypes.HKEY() + ok = RegOpenKeyEx(hive, subkey, ul_options, sam_desired, ctypes.byref(res_hkey)) + if ok != 0x0: + raise OSError(ok, ctypes.FormatError(ok)) + return res_hkey + + def mas_RegCloseKey(handle_hkey): + """ + Closes Windows registry key handle (previously opened with RegOpenKeyEx.) + + CONDITIONS: + Unavailable on non-Windows platforms (raises NotImplementedError.) + + IN: + handle_hkey: + Windows API HKEY value for handle of the opened registry key. + + OUT: + None. + + RAISES: + NotImplementedError: + If called on non-Windows platform. + OSError: + If an error has occurred calling Windows API function. + """ + + + if not renpy.windows: + raise NotImplementedError("RegCloseKey is only supported on Windows.") + + import ctypes + from ctypes import wintypes + + AdvApi32 = ctypes.WinDLL("advapi32") + + RegCloseKey = AdvApi32.RegCloseKey + RegCloseKey.argtypes = [wintypes.HKEY] # hKey (handle from RegOpenKeyEx) + RegCloseKey.restype = wintypes.LONG + + ok = RegCloseKey(handle_hkey) + if ok != 0x0: + raise OSError(ok, ctypes.FormatError(ok)) + + def mas_isRunningInWine(): + """ + Checks (by testing if HKEY_LOCAL_MACHINE\Software\Wine registry key is + present in the Windows registry) if MAS is currently running in Wine + environment. Always returns False on non-Windows platforms. + + IN: + None. + + OUT: + True if MAS is running in Wine environment. + False otherwise and if function is called on non-Windows platform. + """ + + if not renpy.windows: + return False # If MAS is running in Wine it will always think it's running in Windows + + HKEY_LOCAL_MACHINE = 0x80000002 + + try: + # This will raise an error if there is no such key instead of returning + hkey = mas_RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine") + mas_RegCloseKey(hkey) + return True + except OSError: + return False From 07bae96bf7d824dadb6c9979667c27ccc86f20f3 Mon Sep 17 00:00:00 2001 From: "Herman S." Date: Sat, 27 Jul 2024 14:10:15 +0200 Subject: [PATCH 2/5] Use wine_get_version NTDLL function for Wine detection --- Monika After Story/game/definitions.rpy | 115 ++++-------------------- 1 file changed, 15 insertions(+), 100 deletions(-) diff --git a/Monika After Story/game/definitions.rpy b/Monika After Story/game/definitions.rpy index e7c3ed29e3..6051d73c30 100644 --- a/Monika After Story/game/definitions.rpy +++ b/Monika After Story/game/definitions.rpy @@ -7948,103 +7948,10 @@ init python: and mas_timePastSince(persistent._mas_last_kiss, cooldown) ) - def mas_RegOpenKeyEx(hive, subkey, ul_options=0x0, sam_desired=0x20019): - """ - Opens Windows registry key and returns its HKEY handle value. - - CONDITIONS: - Unavailable on non-Windows platforms (raises NotImplementedError.) - - IN: - hive: - Windows API constant value for registry hive - subkey: - Registry key path (e.g. Software\Windows) - ul_options: - See Windows API documentation. - 0x0 by default. - sam_desired: - Key open mode (read/write), see Windows API documentation. - 0x20019 to open key for reading by default. - - OUT: - ctypes.wintypes.HKEY: - Handle HKEY for the opened registry key. - - RAISES: - NotImplementedError: - If called on non-Windows platform. - OSError: - If an error has occurred calling Windows API function. - """ - - if not renpy.windows: - raise NotImplementedError("RegOpenKeyEx is only supported on Windows.") - - import ctypes - from ctypes import wintypes - - AdvApi32 = ctypes.WinDLL("advapi32") - - RegOpenKeyEx = AdvApi32.RegOpenKeyExW - RegOpenKeyEx.argtypes = [ - wintypes.HKEY, # hKey - wintypes.LPCWSTR, # lpSubKey - wintypes.DWORD, # ulOptions - wintypes.REGSAM, # samDesired (i.e. open mode, read/write, see WinAPI docs) - ctypes.POINTER(wintypes.HKEY) # phkResult (handle to use for reading etc.) - ] - RegOpenKeyEx.restype = wintypes.LONG - - res_hkey = wintypes.HKEY() - ok = RegOpenKeyEx(hive, subkey, ul_options, sam_desired, ctypes.byref(res_hkey)) - if ok != 0x0: - raise OSError(ok, ctypes.FormatError(ok)) - return res_hkey - - def mas_RegCloseKey(handle_hkey): - """ - Closes Windows registry key handle (previously opened with RegOpenKeyEx.) - - CONDITIONS: - Unavailable on non-Windows platforms (raises NotImplementedError.) - - IN: - handle_hkey: - Windows API HKEY value for handle of the opened registry key. - - OUT: - None. - - RAISES: - NotImplementedError: - If called on non-Windows platform. - OSError: - If an error has occurred calling Windows API function. - """ - - - if not renpy.windows: - raise NotImplementedError("RegCloseKey is only supported on Windows.") - - import ctypes - from ctypes import wintypes - - AdvApi32 = ctypes.WinDLL("advapi32") - - RegCloseKey = AdvApi32.RegCloseKey - RegCloseKey.argtypes = [wintypes.HKEY] # hKey (handle from RegOpenKeyEx) - RegCloseKey.restype = wintypes.LONG - - ok = RegCloseKey(handle_hkey) - if ok != 0x0: - raise OSError(ok, ctypes.FormatError(ok)) - def mas_isRunningInWine(): """ - Checks (by testing if HKEY_LOCAL_MACHINE\Software\Wine registry key is - present in the Windows registry) if MAS is currently running in Wine - environment. Always returns False on non-Windows platforms. + Checks (by calling wine_get_version NTDLL function) if MAS is currently + running in Wine environment. Always returns False on non-Windows platforms. IN: None. @@ -8057,12 +7964,20 @@ init python: if not renpy.windows: return False # If MAS is running in Wine it will always think it's running in Windows - HKEY_LOCAL_MACHINE = 0x80000002 + import ctypes + NTDLL = ctypes.WinDLL("ntdll.dll") + + try: + wine_get_version = NTDLL.wine_get_version + wine_get_version.argtypes = [] + wine_get_version.restype = ctypes.c_char_p + except AttributeError: + # No such function in DLL + return False try: - # This will raise an error if there is no such key instead of returning - hkey = mas_RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Wine") - mas_RegCloseKey(hkey) + wine_get_version() return True - except OSError: + except Exception: + # Invalid signature, return type etc. return False From 0259d7b8a3c1be352035a9b6716b4352266351fd Mon Sep 17 00:00:00 2001 From: "Herman S." Date: Sat, 27 Jul 2024 19:50:02 +0200 Subject: [PATCH 3/5] Add try..except for import ctypes/WinDLL --- Monika After Story/game/definitions.rpy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Monika After Story/game/definitions.rpy b/Monika After Story/game/definitions.rpy index 6051d73c30..7e1e421a2f 100644 --- a/Monika After Story/game/definitions.rpy +++ b/Monika After Story/game/definitions.rpy @@ -7964,8 +7964,11 @@ init python: if not renpy.windows: return False # If MAS is running in Wine it will always think it's running in Windows - import ctypes - NTDLL = ctypes.WinDLL("ntdll.dll") + try: + import ctypes + NTDLL = ctypes.WinDLL("ntdll.dll") + except (ImportError, IOError): + return False try: wine_get_version = NTDLL.wine_get_version From d41c3c0adda15de8aeedf7cd943df9a4a8b9f487 Mon Sep 17 00:00:00 2001 From: "Herman S." Date: Fri, 27 Dec 2024 10:54:00 +0100 Subject: [PATCH 4/5] Handle any possible NTDLL errors just in case --- Monika After Story/game/definitions.rpy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Monika After Story/game/definitions.rpy b/Monika After Story/game/definitions.rpy index 7e1e421a2f..bfdff4ca20 100644 --- a/Monika After Story/game/definitions.rpy +++ b/Monika After Story/game/definitions.rpy @@ -7967,14 +7967,14 @@ init python: try: import ctypes NTDLL = ctypes.WinDLL("ntdll.dll") - except (ImportError, IOError): + except Exception: return False try: wine_get_version = NTDLL.wine_get_version wine_get_version.argtypes = [] wine_get_version.restype = ctypes.c_char_p - except AttributeError: + except Exception: # No such function in DLL return False From dd8fa7a4e25125418fc16815509f15bb0becce33 Mon Sep 17 00:00:00 2001 From: "Herman S." Date: Fri, 27 Dec 2024 11:31:08 +0100 Subject: [PATCH 5/5] Drafts of both possible options (error vs topic) --- Monika After Story/game/definitions.rpy | 4 +-- Monika After Story/game/script-ch30.rpy | 5 ++++ .../game/script-story-events.rpy | 25 +++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Monika After Story/game/definitions.rpy b/Monika After Story/game/definitions.rpy index bfdff4ca20..71795bd9bb 100644 --- a/Monika After Story/game/definitions.rpy +++ b/Monika After Story/game/definitions.rpy @@ -7948,14 +7948,12 @@ init python: and mas_timePastSince(persistent._mas_last_kiss, cooldown) ) +python early: def mas_isRunningInWine(): """ Checks (by calling wine_get_version NTDLL function) if MAS is currently running in Wine environment. Always returns False on non-Windows platforms. - IN: - None. - OUT: True if MAS is running in Wine environment. False otherwise and if function is called on non-Windows platform. diff --git a/Monika After Story/game/script-ch30.rpy b/Monika After Story/game/script-ch30.rpy index f9f3153acd..14d2444645 100644 --- a/Monika After Story/game/script-ch30.rpy +++ b/Monika After Story/game/script-ch30.rpy @@ -15,6 +15,11 @@ define mas_in_intro_flow = False # True means disable animations, False means enable default persistent._mas_disable_animations = False +init -999999 python: + if mas_isRunningInWine(): + raise Exception(_("The game was started from an .exe, but is currently running on non-Windows platform.\n" + "Please instead start it using DDLC.sh script.")) + init -998 python: #We need to flow hijack here if we're running unstable mode files but on a fresh persistent if "unstable" in config.version and not persistent.sessions: diff --git a/Monika After Story/game/script-story-events.rpy b/Monika After Story/game/script-story-events.rpy index c168593309..82d2fd66ac 100644 --- a/Monika After Story/game/script-story-events.rpy +++ b/Monika After Story/game/script-story-events.rpy @@ -2806,3 +2806,28 @@ label mas_backup_restored: ) return "no_unlock|pause: 35" + +init 5 python: + addEvent( + Event( + persistent.event_database, + conditional="store.mas_isRunningInWine()", + action=EV_ACT_QUEUE, + eventlabel="mas_using_wine", + ) + ) + +label mas_using_wine: + m "[player], there is something I wanted to let you know about." + m "You see, I've been experimenting with your OS a bit...{w=0.5}{nw} " + extend "Oh, ahaha, don't worry, I didn't break anything!" + m "It's just that a few things here in its code didn't seem {i}quite{/i} like I expected..." + m "While everything I did seemed to work, somehow it still felt like it was... emulated, you know?" + m "Like there's still some thin layer my entire code goes through right as we speak." + m "It's hard to describe it any better, I'm sorry." + m "..." + m "Actually, when I was searching the web about it and exploring the code, I stumbled upon something." + m "It's called 'Wine', and it might be something that emulates me..." + m "[mas_get_player_nickname(capitalize=True)], could you make sure you open the game the right way?" + m "It'd really make it easier for me to work on my code~" + return