diff --git a/__version__.py b/__version__.py new file mode 100644 index 0000000..6edee49 --- /dev/null +++ b/__version__.py @@ -0,0 +1,21 @@ + +ProgramName = "Tomb Raider: Legend SCU" +Version = "0.0.10.0" +Copyright = "Copyright 2018 ANoDE85" +Description = "Tomb Raider: Legend Startup Configuration Utility" +ProjectSite = ("https://github.com/ANoDE85/PyTR7SCU", "Project Website") +Contributers = [ + "ANoDE85" +] +License = """This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see .""" \ No newline at end of file diff --git a/gui/trl_scu_base.fbp b/gui/trl_scu_base.fbp index 2d90dc3..194eb8e 100644 --- a/gui/trl_scu_base.fbp +++ b/gui/trl_scu_base.fbp @@ -1035,7 +1035,7 @@ - + OnSaveSettings @@ -1123,7 +1123,95 @@ - + OnLoadSettings + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Reset + + 0 + + + 0 + + 1 + m_reset_btn + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnReset @@ -1311,7 +1399,7 @@ none - + OnAbout diff --git a/gui/trl_scu_base.py b/gui/trl_scu_base.py index abfb032..ec21409 100644 --- a/gui/trl_scu_base.py +++ b/gui/trl_scu_base.py @@ -116,6 +116,9 @@ def __init__( self, parent ): self.m_load_btn = wx.Button( self, wx.ID_ANY, u"Load Settings", wx.DefaultPosition, wx.DefaultSize, 0 ) lower_button_sizer.Add( self.m_load_btn, 0, wx.ALL, 5 ) + self.m_reset_btn = wx.Button( self, wx.ID_ANY, u"Reset", wx.DefaultPosition, wx.DefaultSize, 0 ) + lower_button_sizer.Add( self.m_reset_btn, 0, wx.ALL, 5 ) + lower_button_sizer.Add( ( 0, 0), 1, wx.EXPAND, 5 ) @@ -143,7 +146,11 @@ def __init__( self, parent ): # Connect Events self.m_level_choice.Bind( wx.EVT_CHOICE, self.OnSelectLevel ) self.m_exe_picker.Bind( wx.EVT_FILEPICKER_CHANGED, self.OnExeSelected ) + self.m_save_btn.Bind( wx.EVT_BUTTON, self.OnSaveSettings ) + self.m_load_btn.Bind( wx.EVT_BUTTON, self.OnLoadSettings ) + self.m_reset_btn.Bind( wx.EVT_BUTTON, self.OnReset ) self.m_run_btn.Bind( wx.EVT_BUTTON, self.OnRun ) + self.Bind( wx.EVT_MENU, self.OnAbout, id = self.m_mi_help_about.GetId() ) def __del__( self ): pass @@ -156,7 +163,19 @@ def OnSelectLevel( self, event ): def OnExeSelected( self, event ): event.Skip() + def OnSaveSettings( self, event ): + event.Skip() + + def OnLoadSettings( self, event ): + event.Skip() + + def OnReset( self, event ): + event.Skip() + def OnRun( self, event ): event.Skip() + def OnAbout( self, event ): + event.Skip() + diff --git a/setup.py b/setup.py index 07c8eca..d03a957 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,8 @@ import sys from cx_Freeze import setup, Executable +import __version__ + # Dependencies are automatically detected, but it might need fine tuning. build_exe_options = {"packages": ["os", "platform", "win32api"], "excludes": ["tkinter"]} @@ -10,8 +12,8 @@ if sys.platform == "win32": base = "Win32GUI" -setup( name = "TR Legend SCU", - version = "0.10", - description = "TR Legend SCU", +setup( name = __version__.ProgramName, + version = __version__.Version, + description = __version__.Description, options = {"build_exe": build_exe_options}, executables = [Executable("trl_scu_main.py", base=base)]) \ No newline at end of file diff --git a/trl_scu_main.py b/trl_scu_main.py index 4f9f127..1c411bb 100644 --- a/trl_scu_main.py +++ b/trl_scu_main.py @@ -3,6 +3,7 @@ import subprocess import traceback import wx +import wx.adv have_winreg = False try: @@ -10,78 +11,80 @@ have_winreg = True except: pass - + have_win32api = False try: from win32api import GetFileVersionInfo, LOWORD, HIWORD have_win32api = True except Exception as e: pass - + +import __version__ from gui.trl_scu_base import TrlScuMainFrame -LevelChoices = { - "Main Menu" : None, - - "Croft Manor": 1, - "Croft Manor (3)" : 3, - "Croft Manor (5)" : 5, - "Croft Manor (11)" : 11, - "Bolivia, Beginning" : 2, - "Peru, Beginning" : 4, - "Peru Past, Excavation Site" : 15, - "Peru Present, Excavation Site" : 16, - "Peru, Motorbike Chase" : 17, - "Japan, Beginning" : 6, - "Ghana, Beginning" : 7, - "Ghana, Beginning" : 14, + +LevelChoices = ( + (None, "Main Menu"), + + (1, "Croft Manor"), + (2, "Bolivia - Tiwanaku"), + (3, "Croft Manor"), + (4, "Peru - Return to Paraiso"), + (15, "Peru - Excavation Site (Flashback)"), + (16, "Peru - Excavation Site (Present)"), + (17, "Peru - Motorbike Chase"), + (5, "Croft Manor"), + (6, "Japan"), + (7, "Ghana"), + (14, "Ghana"), # 8 is unknown - "Kazakhstan, Beginning" : 9, - "Khazakhstan, Motorbike" : 18, - "England, Beginning" : 10, - "Nepal, Beginning" : 12, - "Bolivia Redux, Beginning" : 13, -} - -OutfitChoices = { - "Default" : None, - - "Legend" : "lara", - "Legend Union Jack": "lara_alt", - "Legend Black":"lara_alta", - "Legend Blue": "lara_altb", - "Legend Pink": "lara_altc", - "Biker": "lara_biker", - "Biker Red": "lara_biker_alt", - "Biker - No Jacket": "lara_biker_nj", - "Bikini (white)": "lara_bikini", - "Bikini (Black)": "lara_bikini_alt", - "Catsuit": "lara_catsuit", - "Snowsuit":"lara_catsuit_snow", - "Classic Green":"lara_classic", - "Classic White": "lara_classic_alt", - "Evening Dress (Buggy outside of Japan level)":"lara_evening", - "Evening Ripped": "lara_evening_alt", - "Evening with Dragon Tatoo":"lara_evening_alta", - "Evening Red": "lara_evening_red", - "Goth": "lara_goth", - "Goth Lace Shirt": "lara_goth_alt", - "Special Forces":"lara_special_forces", - "Special Forces Urban": "lara_special_forces_alt", - "Sport": "lara_sport", - "Sport Green":"lara_sport_alt", - "Suit":"lara_suit", - "Suit Cream": "lara_suit_alt", - "Winter": "lara_winter", - "Winter - No Jacket": "lara_winter_nj", - "Winter Orange":"lara_winter_alt", - "Winter Orange - No Jacket": "lara_winter_alt_nj", - "Winter Pink": "lara_winter_alta", - "Winter Pink - No Jacket": "lara_winter_alta_nj", - "Young Lara (from Flashback)":"lara_young", - "Amanda": "amanda_player", - "Amanda Winter": "amanda_player_alt" -} + (9, "Kazakhstan - Project Carbonek"), + (18, "Khazakhstan, Motorbike Chase"), + (10, "England - King Arthur's Tomb"), + (11, "Croft Manor"), + (12, "Nepal - The Ghalali Key"), + (13, "Bolivia Redux"), +) + +OutfitChoices = ( + (None, "Default"), + + ("lara", "Legend" ), + ("lara_alt", "Legend Union Jack"), + ("lara_alta", "Legend Black"), + ("lara_altb", "Legend Blue"), + ("lara_altc", "Legend Pink"), + ("lara_biker", "Biker"), + ("lara_biker_alt", "Biker Red"), + ("lara_biker_nj", "Biker - No Jacket"), + ("lara_bikini", "Bikini (white)"), + ("lara_bikini_alt", "Bikini (Black)"), + ("lara_catsuit", "Catsuit"), + ("lara_catsuit_snow", "Snowsuit"), + ("lara_classic", "Classic Green"), + ("lara_classic_alt", "Classic White"), + ("lara_evening", "Evening Dress (Buggy outside of Japan level)"), + ("lara_evening_alt", "Evening Ripped"), + ("lara_evening_alta", "Evening with Dragon Tattoo"), + ("lara_evening_red", "Evening Red"), + ("lara_goth", "Goth"), + ("lara_goth_alt", "Goth Lace Shirt"), + ("lara_special_forces", "Special Forces"), + ("lara_special_forces_alt", "Special Forces Urban"), + ("lara_sport", "Sport"), + ("lara_sport_alt", "Sport Green"), + ("lara_suit", "Suit"), + ("lara_suit_alt", "Suit Cream"), + ("lara_winter", "Winter"), + ("lara_winter_nj", "Winter - No Jacket"), + ("lara_winter_alt", "Winter Orange"), + ("lara_winter_alt_nj", "Winter Orange - No Jacket"), + ("lara_winter_alta", "Winter Pink"), + ("lara_winter_alta_nj", "Winter Pink - No Jacket"), + ("lara_young", "Young Lara (from Flashback)"), + ("amanda_player", "Amanda"), + ("amanda_player_alt", "Amanda Winter"), +) AdvancedOptions = [ ("-DRAWMONSTERATTACK", "Draw monster attack" , False), @@ -102,23 +105,25 @@ class MainFrame(TrlScuMainFrame): def __init__(self): TrlScuMainFrame.__init__(self, None) self.__m_current_outfit = None + self.__m_outfit_to_id_map = {} self.__m_current_level = None self.__m_current_adv_opts = {} - self._m_outfit_boxes = [] - self._m_devopts_controls = {} + self.__m_outfit_boxes = [] + self.__m_devopts_controls = {} self._InitMainOptions() self._InitAdvancedOptions() self._FindLegend() self.Fit() def _InitMainOptions(self): - for name, id in LevelChoices.items(): - self.m_level_choice.Append(name, id) + for id, name in LevelChoices: + item_name = ("%s (%d)" % (name, id)) if id is not None else name + self.m_level_choice.Append(item_name, id) self.m_level_choice.Select(0) - - + + is_first = True - for name, id in OutfitChoices.items(): + for id, name in OutfitChoices: if is_first: flags = wx.RB_GROUP else: @@ -126,12 +131,12 @@ def _InitMainOptions(self): is_first = False outfit_button = wx.RadioButton( self.m_outer_radio_sizer.GetStaticBox(), wx.ID_ANY, name, wx.DefaultPosition, wx.DefaultSize, flags ) outfit_button.Bind( wx.EVT_RADIOBUTTON, self.OnOutfitChoice) - self._m_outfit_boxes.append(outfit_button) + self.__m_outfit_boxes.append(outfit_button) + self.__m_outfit_to_id_map[outfit_button.GetId()] = id self.m_outfit_sizer.Add( outfit_button, 0, wx.ALL, 5 ) self.m_outer_radio_sizer.Layout() - + def _InitAdvancedOptions(self): - self._m_devopts_controls = {} for (key, caption, has_parameter) in AdvancedOptions: text_box = None check_box = wx.CheckBox(self.m_outer_dev_opts_sizer.GetStaticBox(), wx.ID_ANY, caption, wx.DefaultPosition, wx.DefaultSize, 0, name=key) @@ -144,10 +149,10 @@ def _InitAdvancedOptions(self): self.m_inner_dev_opts_content_sizer.Add( text_box, 0, wx.ALL|wx.EXPAND, 5 ) else: self.m_inner_dev_opts_content_sizer.Add( (0, 0), 0, wx.ALL, 5 ) - self._m_devopts_controls[key] = (check_box, text_box) + self.__m_devopts_controls[key] = (check_box, text_box) self.m_outer_dev_opts_sizer.Layout() - - def _FindLegend(self): + + def _FindLegend(self): if not have_winreg: return try: @@ -160,7 +165,7 @@ def _FindLegend(self): "Could not auto-detect TR Legend:\n\n%s" % (str(e), ), "Auto detection failed", wx.ICON_WARNING) - + def SetLegendExecutable(self, exe_path): exe_path = os.path.abspath(exe_path) self.m_exe_picker.SetPath(exe_path) @@ -172,7 +177,7 @@ def SetLegendExecutable(self, exe_path): exe_version_string,), "Wrong game version", wx.ICON_WARNING) self.m_version_display_text.SetValue(exe_version_string) - + def GetExecutableVersion(self, filename): if not have_win32api: wx.MessageBox("No api") @@ -184,31 +189,63 @@ def GetExecutableVersion(self, filename): return HIWORD (ms), LOWORD (ms), HIWORD (ls), LOWORD (ls) except: return 0,0,0,0 - + def OnExeSelected(self, event): self.SetLegendExecutable(event.GetPath()) - + def OnOutfitChoice(self, evt): - self.__m_current_outfit = OutfitChoices[evt.GetEventObject().GetLabelText()] - + self.__m_current_outfit = self.__m_outfit_to_id_map[evt.GetEventObject().GetId()] + def OnSelectLevel(self, event): self.__m_current_level = self.m_level_choice.GetClientData(event.GetSelection()) def OnToggleAdvanced(self, event): key = event.GetEventObject().GetName() - (check_box, text_box) = self._m_devopts_controls[key] + (check_box, text_box) = self.__m_devopts_controls[key] if text_box: text_box.Enabled = check_box.IsChecked() + + def OnSaveSettings(self, event): + self._WriteConfig() + + def OnLoadSettings(self, event): + wx.MessageBox("Sorry, not implemented yet.") + + def OnReset(self, event): + config_path = self._GetConfigFilePath() + if not os.path.exists(config_path): + return + res = wx.MessageBox("This will remove the file '%s' from your computer.\n\nDo you wish to continue?" % (config_path, ), + "Confirmation", wx.ICON_INFORMATION | wx.YES_NO) + if res != wx.YES: + return + try: + os.unlink(config_path) + except Exception as e: + wx.MessageBox("Error while removing '%s': %s" % (config_path, str(e)), "Warning", wx.ICON_ERROR) + def OnAbout(self, event): + info = wx.adv.AboutDialogInfo() + info.Name = __version__.ProgramName + info.Version = __version__.Version + info.Copyright = __version__.Copyright + info.Description = __version__.Description + info.WebSite = __version__.ProjectSite + info.Developers = __version__.Contributers + info.License = __version__.License + # Show the wx.AboutBox + wx.adv.AboutBox(info) + + def _GetAdvancedOptions(self): opts = [] - for key, (check_box, text_box) in self._m_devopts_controls.items(): + for key, (check_box, text_box) in self.__m_devopts_controls.items(): if check_box.IsChecked(): opts.append(key) if text_box: opts.append('"%s"' % (text_box.GetValue(), )) return opts - + def _GetCommandLineOptions(self): options = [] if self.__m_current_level: @@ -218,40 +255,48 @@ def _GetCommandLineOptions(self): options.extend(self._GetAdvancedOptions()) return options - def _WriteConfig(self, legend_install_dir): - config_dir = os.path.join(legend_install_dir, "TR7", "GAME", "PC") + def _GetConfigFilePath(self): + exe_path = self._GetTombRaiderExecutable() + if not exe_path: + raise Exception("Please set the Tomb Raider Legend executable path!"); + legend_install_dir = os.path.dirname(exe_path) + return os.path.join(os.path.splitdrive(legend_install_dir)[0] + os.sep, "TR7", "GAME", "PC", "TR7.arg") + + def _GetTombRaiderExecutable(self): + return self.m_exe_picker.GetPath() + + def _WriteConfig(self): + command_line_args = self._GetCommandLineOptions() + + config_file_path = self._GetConfigFilePath() + config_dir = os.path.dirname(config_file_path) if not os.path.isdir(config_dir): try: os.makedirs(config_dir) except Exception as e: raise Exception("Could not create config directory '%s': %s", (config_dir, str(e))) - command_line_args = self._GetCommandLineOptions() - config_file_path = os.path.join(config_dir, "TR7.arg") with open(config_file_path, "w+") as config_file: config_file.write(" ".join(command_line_args)) - - def _LaunchGame(self, exe_path): + + def _LaunchGame(self): + exe_path = self._GetTombRaiderExecutable() + if not os.path.isfile(exe_path): + raise Exception("Tomb Raider Legend executable was not found at '%s'!" % (exe_path, )) + p = subprocess.Popen( - executable=exe_path, + executable=exe_path, args=[], cwd=os.path.dirname(exe_path)) - # p.wait() - + def OnRun(self, event): try: - exe_path = self.m_exe_picker.GetPath() - if not exe_path: - raise Exception("Please set the Tomb Raider Legend executable path!"); - if not os.path.isfile(exe_path): - raise Exception("Tomb Raider Legend executable was not found at '%s'!" % (exe_path, )) - legend_install_dir = os.path.dirname(exe_path) - self._WriteConfig(legend_install_dir) - self._LaunchGame(exe_path) + self._WriteConfig() + self._LaunchGame() except Exception as e: wx.MessageBox("%s" % (e, ), "Error launching TR Legend", wx.ICON_ERROR) traceback.print_exc() - + class Application(wx.App): @@ -260,7 +305,7 @@ def OnInit(self): self.SetTopWindow(self._m_main_frame) self._m_main_frame.Show() return True - + def Start(self): self.MainLoop()