Skip to content

Instantly share code, notes, and snippets.

@cetteup
Created April 16, 2024 16:36
Show Gist options
  • Save cetteup/df0a065b3b78d446d05d16670c711aa5 to your computer and use it in GitHub Desktop.
Save cetteup/df0a065b3b78d446d05d16670c711aa5 to your computer and use it in GitHub Desktop.
BF2mld - Battlefield 2 mapload-delayer 0.8.2 ("decompiled" source code)
# 2024.04.16 16:32:56 UTC
# Embedded file name: bf2mld_gui.py
import wx
import wx.html
import sys
import time
from _winreg import *
import psutil
import threading
import ConfigParser
import winsound
import os
from wx.lib.embeddedimage import PyEmbeddedImage
from win32event import CreateMutex
from win32api import CloseHandle, GetLastError
from winerror import ERROR_ALREADY_EXISTS
icon = PyEmbeddedImage('iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAR5JREFUWIXtV0sOwiAQfQMaTbTew7UL4w1ceR0PYTyKB+nGm7hSEzd1XOgoQUpBiySmj5DQYei8zi+USGnkhMpqvSPQEQDQAwC+ViwCUprsZ/OAT9elf5yPGACK8kzmWvYbPWAbaANCJIhAW4bq9oIIpPCCwEmAlCY7lnUQXd8ZibkZe0Evhm0sXAZtZC/D7AScIYhJuk/6gIkgD4QmpA1fGUYRSAFJ0J/0AXGfqxydOSAuDzEcEp6xYdAuzexVkIzAtlzxbj3HYTb1Kz4upSyTlH6T1clFNuQh4zFIaYBfY79Z8mkxeera8+s+EAO7LIvyTEn7QAgaCaQ0DgDUxo/JoOrzBZf7C7Umroxrm/Z/wP+WYShaCcE3yO6BjsANB/Z+TqpKtt4AAAAASUVORK5CYII=')
bf2_pid = ''
mymain_stop = threading.Event()
bf2_watchdog_mapload_stop = threading.Event()
configpath = os.getcwd() + '\\bf2mld.cfg'
config = ConfigParser.ConfigParser()
bf2_path = ''
predelay = 1
delay = 3
verbose = False
sound = False
class singleinstance():
""" Limits application to single instance """
def __init__(self):
self.mutexname = 'bf2mld_single_instance_helper_mutex'
self.mutex = CreateMutex(None, False, self.mutexname)
self.lasterror = GetLastError()
print self.lasterror
return
def alreadyrunning(self):
return self.lasterror == ERROR_ALREADY_EXISTS
def __del__(self):
if self.mutex:
CloseHandle(self.mutex)
aboutText = '<h3>Info</h3>\n<p><b>BF2mld version:</b> %(bf2lmd_ver)s<br>\n<b>Python version:</b> %(python_ver)s<br>\n<b>wxPython version:</b> %(wxpython_ver)s<br>\n<b>Developed by:</b> Antiglucke\n</p>\n<h3>Help</h3>\n<p><b>Manual:</b> <a href="%(manual_offline)s">Local</a> or <a href="%(manual_online)s">Online</a><br>\n<b>Direct help:</b> <a href="%(direct_help)s">Go to the forum</a><br>\n<b>Update available?:</b> <a href="%(newest_version)s">Check here</a><br>\n<b>Message to the dev:</b> <a href="%(2f4y_pm)s">PM at 2F4Y.com</a> or via <a href="%(dev-mail)s">E-Mail</a><br>\n</p>'
class HtmlWindow(wx.html.HtmlWindow):
def __init__(self, parent, id, size = (400, 400)):
wx.html.HtmlWindow.__init__(self, parent, id, size=size)
def OnLinkClicked(self, link):
os.startfile(link.GetHref())
class AboutBox(wx.Dialog):
def __init__(self):
wx.Dialog.__init__(self, None, -1, 'About BF2mld', size=(400, 300), style=wx.CAPTION | wx.CLOSE_BOX)
hwin = HtmlWindow(self, -1, size=(400, 400))
vars = {}
vars['bf2lmd_ver'] = '0.8.2'
vars['python_ver'] = sys.version.split()[0]
vars['wxpython_ver'] = wx.VERSION_STRING
vars['manual_offline'] = os.getcwd() + '\\BF2mld_manual.pdf'
vars['manual_online'] = 'http://fiftytwelve.com/BF2mld/BF2mld_manual_latest.pdf'
vars['direct_help'] = 'http://2f4y.com/forum/viewthread.php?forum_id=5&thread_id=2180'
vars['newest_version'] = 'http://fiftytwelve.com/BF2mld/'
vars['2f4y_pm'] = 'http://2f4y.com/messages.php?msg_send=108'
vars['dev-mail'] = 'mailto:[email protected]?subject=BF2mld'
hwin.SetPage(aboutText % vars)
return
class TaskBarIcon(wx.TaskBarIcon):
def __init__(self, frame):
wx.TaskBarIcon.__init__(self)
self.frame = frame
self.SetIcon(icon.getIcon(), 'BF2mld')
self.Bind(wx.EVT_TASKBAR_LEFT_DOWN, self.OnTaskBarClick)
self.Bind(wx.EVT_TASKBAR_RIGHT_DOWN, self.OnTaskBarClick)
def OnTaskBarClick(self, evt):
if self.frame.IsIconized():
self.frame.Iconize(False)
if not self.frame.IsShown():
self.frame.Show()
self.frame.Raise()
class Frame(wx.Frame):
def __init__(self, title):
global sound
global delay
global predelay
global verbose
global bf2_path
wx.Frame.__init__(self, None, title=title, size=(400, 500), style=wx.MINIMIZE_BOX | wx.CAPTION | wx.CLOSE_BOX)
self.SetIcon(icon.getIcon())
self.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_BTNFACE))
self.tbIcon = TaskBarIcon(self)
self.Bind(wx.EVT_ICONIZE, self.OnMinimize)
self.Bind(wx.EVT_CLOSE, self.OnClose)
self.bs_bf2path = wx.BoxSizer(wx.HORIZONTAL)
self.lbl_bf2path = wx.StaticText(self, -1, 'BF2 path:')
self.lbl_bf2path.SetMinSize((50, -1))
self.lbl_bf2path.SetToolTip(wx.ToolTip('path to the BF2 executable'))
self.fp_bf2path = wx.FilePickerCtrl(self, wx.ID_ANY, '', 'Locate the BF2 executable...', 'BF2 executable (*.exe)|*.exe', (-1, -1), (-1, -1), wx.FLP_USE_TEXTCTRL | wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
self.fp_bf2path.SetToolTip(wx.ToolTip('path to the BF2 executable'))
self.bs_bf2path.Add(self.lbl_bf2path, 0, wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5)
self.bs_bf2path.Add(self.fp_bf2path, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5)
self.lbl_predelay = wx.StaticText(self, wx.ID_ANY, 'Predelay:')
self.lbl_predelay.SetMinSize((50, -1))
self.lbl_predelay.SetToolTip(wx.ToolTip('time (seconds) while the endscreen is shown,\nbefore the delay is applied'))
self.sc_predelay = wx.SpinCtrl(self, wx.ID_ANY, '', (-1, -1), (-1, -1), wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT, 0, 60, 1)
self.sc_predelay.SetMinSize((60, -1))
self.sc_predelay.SetToolTip(wx.ToolTip('time (seconds) while the endscreen is shown,\nbefore the delay is applied'))
self.lbl_delay = wx.StaticText(self, wx.ID_ANY, 'Delay:')
self.lbl_delay.SetToolTip(wx.ToolTip('time (seconds) that the mapload gets delayed for'))
self.sc_delay = wx.SpinCtrl(self, wx.ID_ANY, '', (-1, -1), (-1, -1), wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT, 1, 60, 3)
self.sc_delay.SetMinSize((60, -1))
self.sc_delay.SetToolTip(wx.ToolTip('time (seconds) that the mapload gets delayed for'))
self.cb_sound = wx.CheckBox(self, wx.ID_ANY, '')
self.cb_sound.SetToolTip(wx.ToolTip('play a sound on [pre]delay for easier configuration'))
self.lbl_sound = wx.StaticText(self, wx.ID_ANY, 'Sound')
self.lbl_sound.SetToolTip(wx.ToolTip('play a sound on [pre]delay for easier configuration'))
self.bs_preadelay = wx.BoxSizer(wx.HORIZONTAL)
self.bs_preadelay.Add(self.lbl_predelay, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.bs_preadelay.Add(self.sc_predelay, 0, wx.ALL, 5)
self.bs_preadelay.AddSpacer(20)
self.bs_preadelay.Add(self.lbl_delay, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.bs_preadelay.Add(self.sc_delay, 0, wx.ALL, 5)
self.bs_preadelay.AddSpacer(20)
self.bs_preadelay.Add(self.cb_sound, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.bs_preadelay.Add(self.lbl_sound, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 5)
self.sbs_settings = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, 'Settings'), orient=wx.VERTICAL)
self.sbs_settings.Add(self.bs_bf2path, 0, wx.EXPAND)
self.sbs_settings.Add(self.bs_preadelay, 0, wx.EXPAND)
self.btn_about = wx.Button(self, wx.ID_ANY, 'About')
self.btn_about.SetToolTip(wx.ToolTip('show help and version info'))
self.btn_startstop = wx.Button(self, wx.ID_ANY, 'Start')
self.btn_startstop.SetToolTip(wx.ToolTip('start/stop the mapload-watchdog'))
self.bs_mid = wx.BoxSizer(wx.HORIZONTAL)
self.bs_mid.Add(self.btn_about, 0, wx.ALL, 5)
self.bs_mid.AddStretchSpacer()
self.bs_mid.Add(self.btn_startstop, 0, wx.ALL, 5)
self.btn_clear = wx.Button(self, wx.ID_ANY, 'Clear')
self.btn_clear.SetToolTip(wx.ToolTip('clear the output'))
self.btn_verbose = wx.Button(self, wx.ID_ANY, 'Verbose')
self.btn_verbose.SetToolTip(wx.ToolTip('activate/deactivate verbose output'))
self.bs_outputbtns = wx.BoxSizer(wx.HORIZONTAL)
self.bs_outputbtns.Add(self.btn_clear, 0, wx.ALL, 5)
self.bs_outputbtns.Add(self.btn_verbose, 0, wx.ALL, 5)
self.bs_outputbtns.AddStretchSpacer()
self.tc_output = wx.TextCtrl(self, wx.ID_ANY, '', (-1, -1), (-1, -1), wx.TE_MULTILINE | wx.TE_READONLY)
self.sbs_output = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, 'Output'), orient=wx.VERTICAL)
self.sbs_output.Add(self.tc_output, 1, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5)
self.sbs_output.Add(self.bs_outputbtns, 0, wx.EXPAND)
self.bs_main = wx.BoxSizer(wx.VERTICAL)
self.bs_main.Add(self.sbs_settings, 0, wx.ALL | wx.EXPAND, 5)
self.bs_main.Add(self.bs_mid, 0, wx.ALL | wx.EXPAND, 0)
self.bs_main.Add(self.sbs_output, 1, wx.ALL | wx.EXPAND, 5)
self.SetSizer(self.bs_main)
self.Layout()
self.Bind(wx.EVT_BUTTON, self.OnStartStop, self.btn_startstop)
self.Bind(wx.EVT_BUTTON, self.OnClear, self.btn_clear)
self.Bind(wx.EVT_BUTTON, self.OnVerbose, self.btn_verbose)
self.Bind(wx.EVT_BUTTON, self.OnAbout, self.btn_about)
self.Bind(wx.EVT_CHECKBOX, self.OnSound, self.cb_sound)
if os.path.isfile(configpath):
self.getconfig()
else:
self.setconfig()
self.fp_bf2path.SetPath(bf2_path)
self.sc_predelay.SetValue(predelay)
self.sc_delay.SetValue(delay)
self.setverbose(verbose)
self.cb_sound.SetValue(sound)
if self.fp_bf2path.GetPath() == '':
bf2_path_tmp = self.get_bf2_installpath()
if bf2_path_tmp:
self.fp_bf2path.SetPath(bf2_path_tmp + '\\BF2.exe')
self.btn_startstop.SetFocus()
if len(sys.argv) > 1:
corrparam = ['-autostart', '+autostart', 'autostart']
if sys.argv[1].lower() in corrparam:
self.OnStartStop('')
return
def printlog(self, msg, source, is_verbose_msg = False):
if not is_verbose_msg:
self.tc_output.AppendText(time.strftime('%x %X: ') + source + ': ' + msg + '\n')
elif verbose:
self.tc_output.AppendText(time.strftime('%x %X: ') + source + ': ' + msg + '\n')
def setconfig(self):
global delay
global sound
global bf2_path
global predelay
try:
self.printlog('Writing config...', 'MAIN', True)
configfile = open(configpath, 'wb')
if not config.has_section('settings'):
config.add_section('settings')
predelay = self.sc_predelay.GetValue()
delay = self.sc_delay.GetValue()
bf2_path = self.fp_bf2path.GetPath()
sound = self.cb_sound.GetValue()
config.set('settings', 'predelay', predelay)
config.set('settings', 'delay', delay)
config.set('settings', 'bf2_path', bf2_path)
config.set('settings', 'verbose', verbose)
config.set('settings', 'sound', sound)
config.write(configfile)
self.printlog('...done.', 'MAIN', True)
return True
except:
self.printlog('Could not write config!', 'MAIN', False)
return False
def getconfig(self):
global delay
global sound
global bf2_path
global predelay
global verbose
try:
config.read(configpath)
self.printlog('Reading config...', 'MAIN', True)
predelay = config.getint('settings', 'predelay')
delay = config.getint('settings', 'delay')
bf2_path = config.get('settings', 'bf2_path')
verbose = config.getboolean('settings', 'verbose')
sound = config.getboolean('settings', 'sound')
self.printlog('...done.', 'MAIN', True)
except ConfigParser.NoSectionError:
self.setconfig()
except:
self.printlog('Could not read config!', 'MAIN', False)
return False
def setverbose(self, value):
if value:
self.btn_verbose.SetLabel('Nonverbose')
self.printlog('Verbose mode activated.', 'MAIN', False)
else:
self.btn_verbose.SetLabel('Verbose')
self.printlog('Verbose mode deactivated.', 'MAIN', False)
def threadisrunning(self, tname):
for t in threading.enumerate():
if t.getName() == tname:
if t.isAlive():
return True
return False
def jointhread(self, tname):
for t in threading.enumerate():
if t.getName() == tname:
t.join()
return True
return False
def OnStartStop(self, event):
if not self.threadisrunning('mymain'):
self.GuiDisable()
if self.check_bf2_path():
self.setconfig()
self.printlog('BF2mld started.', 'MAIN', False)
mymain_stop.clear()
t = threading.Thread(target=self.mymain, name='mymain')
t.start()
else:
self.GuiEnable()
else:
mymain_stop.set()
bf2_watchdog_mapload_stop.set()
if self.threadisrunning('bf2_watchdog_mapload'):
self.jointhread('bf2_watchdog_mapload')
if self.threadisrunning('mymain'):
self.jointhread('mymain')
self.printlog('BF2mld stopped.', 'MAIN', False)
self.GuiEnable()
def GuiDisable(self):
self.lbl_bf2path.Disable()
self.fp_bf2path.Disable()
self.lbl_predelay.Disable()
self.sc_predelay.Disable()
self.lbl_delay.Disable()
self.sc_delay.Disable()
self.cb_sound.Disable()
self.lbl_sound.Disable()
self.btn_startstop.SetLabel('Stop')
def GuiEnable(self):
self.lbl_bf2path.Enable()
self.fp_bf2path.Enable()
self.lbl_predelay.Enable()
self.sc_predelay.Enable()
self.lbl_delay.Enable()
self.sc_delay.Enable()
self.cb_sound.Enable()
self.lbl_sound.Enable()
self.btn_startstop.SetLabel('Start')
def OnClear(self, event):
self.tc_output.Clear()
self.printlog('Output cleared.', 'MAIN', False)
def OnVerbose(self, event):
global verbose
if not verbose:
verbose = True
self.setverbose(True)
else:
verbose = False
self.setverbose(False)
def OnMinimize(self, event):
self.Hide()
def OnClose(self, event):
dlg = wx.MessageDialog(self, 'Do you really want to close BF2mld?', 'Confirm Exit', wx.OK | wx.CANCEL | wx.ICON_EXCLAMATION)
result = dlg.ShowModal()
dlg.Destroy()
if result == wx.ID_OK:
mymain_stop.set()
bf2_watchdog_mapload_stop.set()
if self.threadisrunning('bf2_watchdog_mapload'):
self.jointhread('bf2_watchdog_mapload')
if self.threadisrunning('mymain'):
self.jointhread('mymain')
self.tbIcon.RemoveIcon()
self.tbIcon.Destroy()
self.Destroy()
def OnSound(self, event):
global sound
if self.cb_sound.GetValue():
winsound.PlaySound('SystemAsterisk', winsound.SND_ALIAS | winsound.SND_ASYNC)
sound = True
else:
sound = False
def OnAbout(self, event):
dlg = AboutBox()
dlg.ShowModal()
dlg.Destroy()
def bf2_watchdog_mapload(self):
bf2_old_map = self.get_bf2_mapname()
if bf2_old_map != '':
while not bf2_watchdog_mapload_stop.isSet():
bf2_current_map = self.get_bf2_mapname()
if bf2_current_map != '':
if bf2_current_map != bf2_old_map:
self.printlog('Mapload detected! Previous map: %s; new map: %s.' % (bf2_old_map, bf2_current_map), 'FWDOG', False)
self.bf2_delay_load()
bf2_old_map = bf2_current_map
time.sleep(1)
else:
self.printlog('No map loaded or BF2 not running any more. Switching to standby-mode.', 'FWDOG', True)
break
else:
self.printlog('No map loaded or BF2 not running any more. Switching to standby-mode.', 'FWDOG', True)
def get_bf2_installpath(self):
try:
winReg = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
except WindowsError:
return False
try:
bf2_registrykey = OpenKey(winReg, 'SOFTWARE\\Electronic Arts\\EA Games\\Battlefield 2')
except WindowsError:
return False
try:
bf2_reg_installdir = QueryValueEx(bf2_registrykey, 'InstallDir')
except WindowsError:
return False
return bf2_reg_installdir[0]
def check_bf2_path(self):
global bf2_exename
bf2_installdir, bf2_exename = os.path.split(self.fp_bf2path.GetPath())
if bf2_installdir == '':
self.printlog('Path to BF2.exe not specified! Start aborted.', 'PCK', False)
return False
if bf2_exename.lower()[-4:] != '.exe':
self.printlog('Path does not specify an executable file! Start aborted.', 'PCK', False)
return False
if not os.path.isfile(bf2_installdir + '\\' + bf2_exename):
if not os.path.exists(bf2_installdir):
self.printlog('Invalid path! %s is not accessible! Start aborted.' % bf2_installdir, 'PCK', False)
else:
self.printlog('BF2-executable (%s) not found! Start aborted.' % bf2_exename, 'PCK', False)
return False
for file in os.listdir(bf2_installdir):
if file == bf2_exename:
return True
self.printlog('BF2-executable (%s) not found! The exe-name is case-sensitive (BF2.exe is not equal to bf2.exe)! Start aborted.' % bf2_exename, 'PCK', False)
return False
def bf2_delay_load(self):
global bf2_pid
try:
bf2_process = psutil.Process(bf2_pid)
except psutil.NoSuchProcess:
return
self.printlog('Pre-mapload phase: waiting %s second(s) until delaying.' % predelay, 'DEL', True)
if sound:
winsound.PlaySound('SystemAsterisk', winsound.SND_ALIAS | winsound.SND_ASYNC)
time.sleep(predelay)
self.printlog('Mapload phase: delaying mapload for a total of %s second(s).' % delay, 'DEL', True)
i = delay
while i > 0:
if i - 3 >= 0:
tmpdelay = 3
i -= 3
else:
tmpdelay = i
i = 0
try:
bf2_process.suspend()
except psutil.NoSuchProcess:
return
except:
self.printlog('Failed to suspend BF2 (PID=%s).' % bf2_pid, 'DEL', False)
return
for j in range(0, tmpdelay):
if bf2_watchdog_mapload_stop.isSet():
break
if sound:
winsound.PlaySound('SystemAsterisk', winsound.SND_ALIAS | winsound.SND_ASYNC)
time.sleep(1)
try:
bf2_process.resume()
except psutil.NoSuchProcess:
return
except:
self.printlog('Failed to resume BF2 (PID=%s).' % bf2_pid, 'DEL', False)
return
time.sleep(0.5)
def get_bf2_pid(self, bf2_exename):
for process in psutil.process_iter():
try:
if process.name() == bf2_exename:
return process.pid
except psutil.AccessDenied:
pass
return ''
def get_bf2_mapname(self):
try:
bf2_process = psutil.Process(bf2_pid)
except psutil.NoSuchProcess:
return ''
try:
bf_opened_files = bf2_process.get_open_files()
except:
return ''
for file in bf_opened_files:
directory, filename = os.path.split(file.path)
if filename == 'client.zip':
return directory[directory.rfind('\\') + 1:]
return ''
def mymain(self):
global bf2_pid
while not mymain_stop.isSet():
bf2_pid = self.get_bf2_pid(bf2_exename)
bf2_watchdog_mapload_stop.clear()
self.printlog('Checking if BF2 is running...', 'BDOG', True)
if bf2_pid != '':
self.printlog('...yes, BF2 is running.', 'BDOG', True)
if not self.threadisrunning('bf2_watchdog_mapload'):
self.printlog('Switching to active-mode.', 'BDOG', True)
t = threading.Thread(target=self.bf2_watchdog_mapload, name='bf2_watchdog_mapload')
t.start()
else:
self.printlog('...no, BF2 is not running.', 'BDOG', True)
if self.threadisrunning('bf2_watchdog_mapload'):
bf2_watchdog_mapload_stop.set()
self.jointhread('bf2_watchdog_mapload')
for i in range(0, 15):
if not mymain_stop.isSet():
time.sleep(1)
else:
return
class MyApp(wx.App):
def OnInit(self):
self.si = singleinstance()
if self.si.alreadyrunning():
dlg = wx.MessageDialog(None, 'BF2mld is already running.', 'BF2mld', wx.OK | wx.ICON_ERROR)
dlg.ShowModal()
dlg.Destroy()
sys.exit(0)
frame = Frame('BF2mld')
frame.Show(True)
self.SetTopWindow(frame)
return True
app = MyApp(0)
app.MainLoop()
# okay decompyling bf2mld_gui.py.pyc
# decompiled 1 files: 1 okay, 0 failed, 0 verify failed
# 2024.04.16 16:32:57 UTC
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment