diff --git a/.gitignore b/.gitignore index 38ad4fd..b2d328c 100644 --- a/.gitignore +++ b/.gitignore @@ -186,6 +186,4 @@ FakesAssemblies/ GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml -/.vs/slnx.sqlite -/.vs/VSWorkspaceState.json -/.vs/Game Backup Monitor/v15 +/.vs diff --git a/GBM/Classes/clsGame.vb b/GBM/Classes/clsGame.vb index 1ee3906..0ea8ec7 100644 --- a/GBM/Classes/clsGame.vb +++ b/GBM/Classes/clsGame.vb @@ -21,7 +21,6 @@ Public Class clsGame Private bMonitorOnly As Boolean = False Private sComments As String = String.Empty Private bIsRegEx As Boolean = False - Private bDuplicate As Boolean = False Private oImportTags As New List(Of Tag) Private bImportUpdate As Boolean = False @@ -46,12 +45,6 @@ Public Class clsGame End Get End Property - ReadOnly Property CompoundKey As String - Get - Return ProcessName & ":" & ID - End Get - End Property - ReadOnly Property CroppedName As String Get If Name.Length > 40 Then @@ -248,27 +241,15 @@ Public Class clsGame End Set End Property - Property Duplicate As Boolean - Get - Return bDuplicate - End Get - Set(value As Boolean) - bDuplicate = value + Property TruePath As String + Set(value As String) + sPath = value End Set - End Property - - ReadOnly Property TruePath As String Get Return sPath End Get End Property - ReadOnly Property TrueProcess As String - Get - Return HandleProcessDuplicates() - End Get - End Property - Property ImportTags As List(Of Tag) Get Return oImportTags @@ -457,18 +438,6 @@ Public Class clsGame Return DirectCast(Me.MemberwiseClone(), clsGame) End Function - Private Function HandleProcessDuplicates() As String - Dim sProcessName As String - - 'Handle Duplicates - sProcessName = Me.ProcessName - If Me.Duplicate Then - sProcessName = Me.ProcessName.Split(":")(0) - End If - - Return sProcessName - End Function - Public Shared Function SetSyncField(ByVal eSyncFields As eOptionalSyncFields, ByVal eSyncField As eOptionalSyncFields) As eOptionalSyncFields Return eSyncFields Or eSyncField End Function diff --git a/GBM/Classes/clsPathVariable.vb b/GBM/Classes/clsPathVariable.vb index 014bcef..70319d4 100644 --- a/GBM/Classes/clsPathVariable.vb +++ b/GBM/Classes/clsPathVariable.vb @@ -24,7 +24,7 @@ ReadOnly Property FormattedName As String Get - Return "*" & sVariableName & "*" + Return "%" & sVariableName & "%" End Get End Property diff --git a/GBM/Forms/frmAddWizard.vb b/GBM/Forms/frmAddWizard.vb index eadf9c0..6066c0d 100644 --- a/GBM/Forms/frmAddWizard.vb +++ b/GBM/Forms/frmAddWizard.vb @@ -416,7 +416,7 @@ Public Class frmAddWizard End If sNewPath = mgrCommon.OpenFileBrowser("Wizard_Process_Path", frmAddWizard_ChooseProcess, "exe", - frmAddWizard_Executable, sDefaultFolder, False, False) + frmAddWizard_Executable, sDefaultFolder, False) If sNewPath <> String.Empty Then txtProcessPath.Text = sNewPath End Sub diff --git a/GBM/Forms/frmAdvancedImport.vb b/GBM/Forms/frmAdvancedImport.vb index f9202e1..a35125b 100644 --- a/GBM/Forms/frmAdvancedImport.vb +++ b/GBM/Forms/frmAdvancedImport.vb @@ -6,6 +6,7 @@ Public Class frmAdvancedImport Private oImportData As ExportData Private hshImportData As Hashtable Private hshFinalData As New Hashtable + Private bModWinConfigsForLinux As Boolean Private bSelectAll As Boolean = True Private bIsLoading As Boolean = False Private iCurrentSort As Integer = 0 @@ -30,6 +31,15 @@ Public Class frmAdvancedImport End Get End Property + Public Property ModWinConfigsForLinux As Boolean + Set(value As Boolean) + bModWinConfigsForLinux = value + End Set + Get + Return bModWinConfigsForLinux + End Get + End Property + Public ReadOnly Property FinalData As Hashtable Get Return hshFinalData @@ -56,6 +66,62 @@ Public Class frmAdvancedImport End If End Sub + Private Sub ModTags(ByRef oTags As List(Of Tag)) + Dim bExists As Boolean + Dim oTag As Tag + Dim oNewTag As Tag + Dim oRemoveTag As New Tag + Dim sTag As String + Dim sAddTags() As String = {"Wine"} + Dim sRemoveTags() As String = {"Official"} + + For Each sTag In sAddTags + bExists = False + For Each oTag In oTags + If oTag.Name = sTag Then + bExists = True + Exit For + End If + Next + If Not bExists Then + oNewTag = New Tag + oNewTag.Name = sTag + oTags.Add(oNewTag) + End If + Next + + For Each sTag In sRemoveTags + bExists = False + For Each oTag In oTags + If oTag.Name = sTag Then + bExists = True + oRemoveTag = oTag + Exit For + End If + Next + If bExists Then + oTags.Remove(oRemoveTag) + End If + Next + + End Sub + + Private Function CheckIgnoreTags(ByVal oTags As List(Of Tag)) As Boolean + Dim oTag As Tag + Dim sTag As String + Dim sIgnoreTags() As String = {"DOSBox", "ScummVM"} + + For Each oTag In oTags + For Each sTag In sIgnoreTags + If oTag.Name = sTag Then + Return False + End If + Next + Next + + Return True + End Function + Private Sub LoadData(Optional ByVal sFilter As String = "", Optional ByVal bAutoDetect As Boolean = False) Dim oApp As clsGame Dim oListViewItem As ListViewItem @@ -75,6 +141,12 @@ Public Class frmAdvancedImport For Each de As DictionaryEntry In ImportData bAddItem = False oApp = DirectCast(de.Value, clsGame) + + 'Run any required tag mods + If ModWinConfigsForLinux Then + ModTags(oApp.ImportTags) + End If + sTags = String.Empty oApp.ImportTags.Sort(AddressOf mgrCommon.CompareImportTagsByName) For Each oTag As Tag In oApp.ImportTags @@ -82,7 +154,7 @@ Public Class frmAdvancedImport Next sTags = sTags.TrimEnd(New Char() {",", " "}) - oListViewItem = New ListViewItem(New String() {oApp.Name, oApp.TrueProcess, sTags}) + oListViewItem = New ListViewItem(New String() {oApp.Name, oApp.ProcessName, sTags}) oListViewItem.Tag = oApp.ID If FinalData.ContainsKey(oApp.ID) Then @@ -114,11 +186,16 @@ Public Class frmAdvancedImport If sFilter = String.Empty Then bAddItem = True Else - If oApp.Name.ToLower.Contains(sFilter.ToLower) Or oApp.TrueProcess.ToLower.Contains(sFilter.ToLower) Or sTags.ToLower.Contains(sFilter.ToLower) Then + If oApp.Name.ToLower.Contains(sFilter.ToLower) Or oApp.ProcessName.ToLower.Contains(sFilter.ToLower) Or sTags.ToLower.Contains(sFilter.ToLower) Then bAddItem = True End If End If + 'Check for hardcoded ignore tags + If bAddItem And ModWinConfigsForLinux Then + bAddItem = CheckIgnoreTags(oApp.ImportTags) + End If + If bAddItem Then If oListViewItem.Checked Then bResetSelectAll = True lstGames.Items.Add(oListViewItem) diff --git a/GBM/Forms/frmChooseGame.vb b/GBM/Forms/frmChooseGame.vb index ef6f040..3a9a18a 100644 --- a/GBM/Forms/frmChooseGame.vb +++ b/GBM/Forms/frmChooseGame.vb @@ -38,7 +38,7 @@ Public Class frmChooseGame End Sub Private Sub SaveSelection() - oGame.ProcessPath = oProcess.GameInfo.ProcessPath + oGame.ProcessPath = oProcess.ProcessPath mgrMonitorList.DoListUpdate(oGame) End Sub diff --git a/GBM/Forms/frmGameManager.Designer.vb b/GBM/Forms/frmGameManager.Designer.vb index 09f743d..2b5f370 100644 --- a/GBM/Forms/frmGameManager.Designer.vb +++ b/GBM/Forms/frmGameManager.Designer.vb @@ -70,6 +70,7 @@ Partial Class frmGameManager Me.lblHours = New System.Windows.Forms.Label() Me.btnTags = New System.Windows.Forms.Button() Me.grpStats = New System.Windows.Forms.GroupBox() + Me.btnImportBackup = New System.Windows.Forms.Button() Me.cboRemoteBackup = New System.Windows.Forms.ComboBox() Me.lblRestorePathData = New System.Windows.Forms.Label() Me.lblBackupFileData = New System.Windows.Forms.Label() @@ -96,6 +97,8 @@ Partial Class frmGameManager Me.btnExport = New System.Windows.Forms.Button() Me.cmsImport = New System.Windows.Forms.ContextMenuStrip(Me.components) Me.cmsOfficial = New System.Windows.Forms.ToolStripMenuItem() + Me.cmsOfficialWindows = New System.Windows.Forms.ToolStripMenuItem() + Me.cmsOfficialLinux = New System.Windows.Forms.ToolStripMenuItem() Me.cmsFile = New System.Windows.Forms.ToolStripMenuItem() Me.txtQuickFilter = New System.Windows.Forms.TextBox() Me.lblQuickFilter = New System.Windows.Forms.Label() @@ -103,6 +106,7 @@ Partial Class frmGameManager Me.cmsDeleteOne = New System.Windows.Forms.ToolStripMenuItem() Me.cmsDeleteAll = New System.Windows.Forms.ToolStripMenuItem() Me.btnProcesses = New System.Windows.Forms.Button() + Me.ttFullPath = New System.Windows.Forms.ToolTip(Me.components) Me.grpConfig.SuspendLayout() CType(Me.nudLimit, System.ComponentModel.ISupportInitialize).BeginInit() Me.grpExtra.SuspendLayout() @@ -142,7 +146,7 @@ Partial Class frmGameManager Me.btnBackup.Location = New System.Drawing.Point(616, 626) Me.btnBackup.Name = "btnBackup" Me.btnBackup.Size = New System.Drawing.Size(75, 23) - Me.btnBackup.TabIndex = 18 + Me.btnBackup.TabIndex = 19 Me.btnBackup.Text = "&Backup" Me.btnBackup.UseVisualStyleBackColor = True ' @@ -152,7 +156,7 @@ Partial Class frmGameManager Me.btnClose.Location = New System.Drawing.Point(697, 626) Me.btnClose.Name = "btnClose" Me.btnClose.Size = New System.Drawing.Size(75, 23) - Me.btnClose.TabIndex = 19 + Me.btnClose.TabIndex = 20 Me.btnClose.Text = "C&lose" Me.btnClose.UseVisualStyleBackColor = True ' @@ -287,7 +291,7 @@ Partial Class frmGameManager Me.btnInclude.Name = "btnInclude" Me.btnInclude.Size = New System.Drawing.Size(175, 23) Me.btnInclude.TabIndex = 12 - Me.btnInclude.Text = "In&clude Items..." + Me.btnInclude.Text = "Incl&ude Items..." Me.btnInclude.UseVisualStyleBackColor = True ' 'txtID @@ -565,13 +569,14 @@ Partial Class frmGameManager Me.btnTags.Location = New System.Drawing.Point(535, 437) Me.btnTags.Name = "btnTags" Me.btnTags.Size = New System.Drawing.Size(75, 23) - Me.btnTags.TabIndex = 12 - Me.btnTags.Text = "Tags..." + Me.btnTags.TabIndex = 13 + Me.btnTags.Text = "&Tags..." Me.btnTags.UseVisualStyleBackColor = True ' 'grpStats ' Me.grpStats.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.grpStats.Controls.Add(Me.btnImportBackup) Me.grpStats.Controls.Add(Me.cboRemoteBackup) Me.grpStats.Controls.Add(Me.lblRestorePathData) Me.grpStats.Controls.Add(Me.lblBackupFileData) @@ -586,10 +591,19 @@ Partial Class frmGameManager Me.grpStats.Location = New System.Drawing.Point(248, 466) Me.grpStats.Name = "grpStats" Me.grpStats.Size = New System.Drawing.Size(525, 154) - Me.grpStats.TabIndex = 15 + Me.grpStats.TabIndex = 16 Me.grpStats.TabStop = False Me.grpStats.Text = "Backup Information" ' + 'btnImportBackup + ' + Me.btnImportBackup.Location = New System.Drawing.Point(9, 125) + Me.btnImportBackup.Name = "btnImportBackup" + Me.btnImportBackup.Size = New System.Drawing.Size(114, 23) + Me.btnImportBackup.TabIndex = 8 + Me.btnImportBackup.Text = "Import B&ackup Files" + Me.btnImportBackup.UseVisualStyleBackColor = True + ' 'cboRemoteBackup ' Me.cboRemoteBackup.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList @@ -664,7 +678,7 @@ Partial Class frmGameManager Me.btnDeleteBackup.Location = New System.Drawing.Point(129, 125) Me.btnDeleteBackup.Name = "btnDeleteBackup" Me.btnDeleteBackup.Size = New System.Drawing.Size(114, 23) - Me.btnDeleteBackup.TabIndex = 8 + Me.btnDeleteBackup.TabIndex = 9 Me.btnDeleteBackup.Text = "&Delete Backup" Me.btnDeleteBackup.UseVisualStyleBackColor = True ' @@ -701,7 +715,7 @@ Partial Class frmGameManager Me.btnMarkAsRestored.Location = New System.Drawing.Point(429, 626) Me.btnMarkAsRestored.Name = "btnMarkAsRestored" Me.btnMarkAsRestored.Size = New System.Drawing.Size(100, 23) - Me.btnMarkAsRestored.TabIndex = 16 + Me.btnMarkAsRestored.TabIndex = 17 Me.btnMarkAsRestored.Text = "&Mark as Restored" Me.btnMarkAsRestored.UseVisualStyleBackColor = True ' @@ -711,7 +725,7 @@ Partial Class frmGameManager Me.btnRestore.Location = New System.Drawing.Point(535, 626) Me.btnRestore.Name = "btnRestore" Me.btnRestore.Size = New System.Drawing.Size(75, 23) - Me.btnRestore.TabIndex = 17 + Me.btnRestore.TabIndex = 18 Me.btnRestore.Text = "&Restore" Me.btnRestore.UseVisualStyleBackColor = True ' @@ -721,7 +735,7 @@ Partial Class frmGameManager Me.btnSave.Location = New System.Drawing.Point(616, 437) Me.btnSave.Name = "btnSave" Me.btnSave.Size = New System.Drawing.Size(75, 23) - Me.btnSave.TabIndex = 13 + Me.btnSave.TabIndex = 14 Me.btnSave.Text = "&Save" Me.btnSave.UseVisualStyleBackColor = True ' @@ -740,8 +754,8 @@ Partial Class frmGameManager Me.btnCancel.Location = New System.Drawing.Point(697, 437) Me.btnCancel.Name = "btnCancel" Me.btnCancel.Size = New System.Drawing.Size(75, 23) - Me.btnCancel.TabIndex = 14 - Me.btnCancel.Text = "&Cancel" + Me.btnCancel.TabIndex = 15 + Me.btnCancel.Text = "Ca&ncel" Me.btnCancel.UseVisualStyleBackColor = True ' 'chkEnabled @@ -838,10 +852,23 @@ Partial Class frmGameManager ' 'cmsOfficial ' + Me.cmsOfficial.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.cmsOfficialWindows, Me.cmsOfficialLinux}) Me.cmsOfficial.Name = "cmsOfficial" Me.cmsOfficial.Size = New System.Drawing.Size(117, 22) Me.cmsOfficial.Text = "&Official List..." ' + 'cmsOfficialWindows + ' + Me.cmsOfficialWindows.Name = "cmsOfficialWindows" + Me.cmsOfficialWindows.Size = New System.Drawing.Size(180, 22) + Me.cmsOfficialWindows.Text = "&Windows..." + ' + 'cmsOfficialLinux + ' + Me.cmsOfficialLinux.Name = "cmsOfficialLinux" + Me.cmsOfficialLinux.Size = New System.Drawing.Size(180, 22) + Me.cmsOfficialLinux.Text = "&Linux..." + ' 'cmsFile ' Me.cmsFile.Name = "cmsFile" @@ -888,10 +915,16 @@ Partial Class frmGameManager Me.btnProcesses.Location = New System.Drawing.Point(454, 437) Me.btnProcesses.Name = "btnProcesses" Me.btnProcesses.Size = New System.Drawing.Size(75, 23) - Me.btnProcesses.TabIndex = 20 - Me.btnProcesses.Text = "Processes..." + Me.btnProcesses.TabIndex = 12 + Me.btnProcesses.Text = "Pro&cesses..." Me.btnProcesses.UseVisualStyleBackColor = True ' + 'ttFullPath + ' + Me.ttFullPath.AutoPopDelay = 5000 + Me.ttFullPath.InitialDelay = 300 + Me.ttFullPath.ReshowDelay = 60 + ' 'frmGameManager ' Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) @@ -1022,4 +1055,8 @@ Partial Class frmGameManager Friend WithEvents chkRegEx As CheckBox Friend WithEvents btnGameID As Button Friend WithEvents btnProcesses As Button + Friend WithEvents ttFullPath As ToolTip + Friend WithEvents btnImportBackup As Button + Friend WithEvents cmsOfficialWindows As ToolStripMenuItem + Friend WithEvents cmsOfficialLinux As ToolStripMenuItem End Class diff --git a/GBM/Forms/frmGameManager.resx b/GBM/Forms/frmGameManager.resx index aa8299a..30ad1df 100644 --- a/GBM/Forms/frmGameManager.resx +++ b/GBM/Forms/frmGameManager.resx @@ -123,4 +123,7 @@ 127, 17 + + 275, 17 + \ No newline at end of file diff --git a/GBM/Forms/frmGameManager.vb b/GBM/Forms/frmGameManager.vb index e1656ae..d45b4b1 100644 --- a/GBM/Forms/frmGameManager.vb +++ b/GBM/Forms/frmGameManager.vb @@ -14,8 +14,10 @@ Public Class frmGameManager Private bDisableExternalFunctions As Boolean = False Private bTriggerBackup As Boolean = False Private bTriggerRestore As Boolean = False + Private bTriggerImportBackup As Boolean = False Private oBackupList As New List(Of clsGame) Private oRestoreList As New Hashtable + Private oImportBackupList As New Hashtable Private oGameData As OrderedDictionary Private oLocalBackupData As SortedList Private oRemoteBackupData As SortedList @@ -121,6 +123,15 @@ Public Class frmGameManager End Set End Property + Property TriggerImportBackup As Boolean + Get + Return bTriggerImportBackup + End Get + Set(value As Boolean) + bTriggerImportBackup = value + End Set + End Property + Property BackupList As List(Of clsGame) Get Return oBackupList @@ -139,6 +150,15 @@ Public Class frmGameManager End Set End Property + Property ImportBackupList As Hashtable + Get + Return oImportBackupList + End Get + Set(value As Hashtable) + oImportBackupList = value + End Set + End Property + Private Property IsDirty As Boolean Get Return bIsDirty @@ -162,14 +182,18 @@ Public Class frmGameManager oLocalBackupData = mgrManifest.ReadLatestManifest(mgrSQLite.Database.Local) End Sub - Private Function ConvertToRelativePath(ByVal sSavePath As String, ByVal sAppPath As String) As String + Private Function HandleSavePath(ByVal sSavePath As String, ByVal sAppPath As String) As String Dim sPath As String = sSavePath - 'Determine a relative path if possible - If sAppPath <> String.Empty And sSavePath <> String.Empty Then - If Not mgrPath.IsAbsolute(sSavePath) Then + If Not mgrPath.IsAbsolute(sSavePath) Then + 'Determine a relative path if possible + If sAppPath <> String.Empty And sSavePath <> String.Empty Then sPath = mgrPath.DetermineRelativePath(sAppPath, sSavePath) End If + Else + If Not oSettings.ShowResolvedPaths Then + sPath = mgrPath.ReverseSpecialPaths(sPath) + End If End If Return sPath @@ -340,7 +364,7 @@ Public Class frmGameManager End If sNewPath = mgrCommon.OpenFileBrowser("GM_Process", frmGameManager_ChooseExe, "exe", - frmGameManager_Executable, sDefaultFolder, False, False) + frmGameManager_Executable, sDefaultFolder, False) If sNewPath <> String.Empty Then txtAppPath.Text = Path.GetDirectoryName(sNewPath) @@ -380,7 +404,7 @@ Public Class frmGameManager If sNewPath <> String.Empty Then txtSavePath.Text = sNewPath - txtSavePath.Text = ConvertToRelativePath(txtSavePath.Text, txtAppPath.Text) + txtSavePath.Text = HandleSavePath(txtSavePath.Text, txtAppPath.Text) End If End Sub @@ -398,10 +422,10 @@ Public Class frmGameManager 'Unix Handler If Not mgrCommon.IsUnix Then sNewPath = mgrCommon.OpenFileBrowser("GM_Icon", frmGameManager_ChooseCustomIcon, "ico", - frmGameManager_Icon, sDefaultFolder, False, False) + frmGameManager_Icon, sDefaultFolder, False) Else sNewPath = mgrCommon.OpenFileBrowser("GM_Icon", frmGameManager_ChooseCustomIcon, "png", - "PNG", sDefaultFolder, False, False) + "PNG", sDefaultFolder, False) End If If sNewPath <> String.Empty Then @@ -574,7 +598,7 @@ Public Class frmGameManager If CurrentGame.ProcessPath <> String.Empty Then CurrentBackupItem.RelativeRestorePath = CurrentGame.ProcessPath & Path.DirectorySeparatorChar & CurrentBackupItem.RestorePath Else - sProcess = CurrentGame.TrueProcess + sProcess = CurrentGame.ProcessName If mgrCommon.IsProcessNotSearchable(CurrentGame) Then bNoAuto = True sRestorePath = mgrPath.ProcessPathSearch(CurrentBackupItem.Name, sProcess, mgrCommon.FormatString(frmGameManager_ErrorPathNotSet, CurrentBackupItem.Name), bNoAuto) @@ -688,25 +712,6 @@ Public Class frmGameManager End Sub - Private Sub UpdateBackupInfo(ByVal sManifestID As String) - Dim sFileName As String - - If sManifestID <> String.Empty Then - CurrentBackupItem = mgrManifest.DoManifestGetByManifestID(sManifestID, mgrSQLite.Database.Remote) - - sFileName = BackupFolder & CurrentBackupItem.FileName - - If File.Exists(sFileName) Then - lblBackupFileData.Text = Path.GetFileName(CurrentBackupItem.FileName) & " (" & mgrCommon.FormatDiskSpace(mgrCommon.GetFileSize(sFileName)) & ")" - Else - lblBackupFileData.Text = frmGameManager_ErrorNoBackupExists - End If - - lblRestorePathData.Text = CurrentBackupItem.RestorePath - End If - - End Sub - Public Sub VerifyBackups(ByVal oApp As clsGame) Dim oCurrentBackup As clsBackup Dim oCurrentBackups As List(Of clsBackup) @@ -737,6 +742,23 @@ Public Class frmGameManager Cursor.Current = Cursors.Default End Sub + Private Sub SetBackupRestorePath(ByVal oApp As clsGame) + Dim sttRestorePath As String + + If Not CurrentBackupItem.AbsolutePath And oApp.ProcessPath <> String.Empty Then + lblRestorePathData.Text = oApp.ProcessPath & Path.DirectorySeparatorChar & CurrentBackupItem.RestorePath + Else + If oSettings.ShowResolvedPaths Then + lblRestorePathData.Text = CurrentBackupItem.RestorePath + sttRestorePath = CurrentBackupItem.TruePath + Else + lblRestorePathData.Text = CurrentBackupItem.TruePath + sttRestorePath = CurrentBackupItem.RestorePath + End If + If CurrentBackupItem.AbsolutePath Then ttFullPath.SetToolTip(lblRestorePathData, sttRestorePath) + End If + End Sub + Private Sub GetBackupInfo(ByVal oApp As clsGame) Dim oBackupInfo As clsBackup Dim oCurrentBackup As clsBackup @@ -746,6 +768,7 @@ Public Class frmGameManager Dim bLocalData As Boolean = False Dim bRemoteData As Boolean = False + 'cboRemoteBackup cboRemoteBackup.ValueMember = "Key" cboRemoteBackup.DisplayMember = "Value" @@ -773,7 +796,7 @@ Public Class frmGameManager lblBackupFileData.Text = frmGameManager_ErrorNoBackupExists End If - lblRestorePathData.Text = CurrentBackupItem.RestorePath + SetBackupRestorePath(oApp) Else oComboItems.Add(New KeyValuePair(Of String, String)(String.Empty, frmGameManager_None)) lblBackupFileData.Text = String.Empty @@ -810,6 +833,24 @@ Public Class frmGameManager End Sub + Private Sub UpdateBackupInfo(ByVal sManifestID As String) + Dim sFileName As String + + If sManifestID <> String.Empty Then + CurrentBackupItem = mgrManifest.DoManifestGetByManifestID(sManifestID, mgrSQLite.Database.Remote) + + sFileName = BackupFolder & CurrentBackupItem.FileName + + If File.Exists(sFileName) Then + lblBackupFileData.Text = Path.GetFileName(CurrentBackupItem.FileName) & " (" & mgrCommon.FormatDiskSpace(mgrCommon.GetFileSize(sFileName)) & ")" + Else + lblBackupFileData.Text = frmGameManager_ErrorNoBackupExists + End If + + SetBackupRestorePath(CurrentGame) + End If + End Sub + Private Sub DeleteAllBackups() Dim oBackupData As List(Of clsBackup) Dim oBackup As clsBackup @@ -860,14 +901,22 @@ Public Class frmGameManager Dim oData As KeyValuePair(Of String, String) = lstGames.SelectedItems(0) Dim oApp As clsGame = DirectCast(GameData(oData.Key), clsGame) + Dim sttPath As String 'Core txtID.Text = oApp.ID txtName.Text = oApp.Name - txtProcess.Text = oApp.TrueProcess + txtProcess.Text = oApp.ProcessName chkRegEx.Checked = oApp.IsRegEx txtParameter.Text = oApp.Parameter - txtSavePath.Text = oApp.Path + If oSettings.ShowResolvedPaths Then + txtSavePath.Text = oApp.Path + sttPath = oApp.TruePath + Else + txtSavePath.Text = oApp.TruePath + sttPath = oApp.Path + End If + If oApp.AbsolutePath Then ttFullPath.SetToolTip(txtSavePath, sttPath) txtFileType.Text = oApp.FileType txtExclude.Text = oApp.ExcludeList chkFolderSave.Checked = oApp.FolderSave @@ -1004,6 +1053,7 @@ Public Class frmGameManager btnBackup.Enabled = False btnMarkAsRestored.Enabled = False btnRestore.Enabled = False + btnImportBackup.Enabled = False btnDeleteBackup.Enabled = False btnOpenBackupFile.Enabled = False btnOpenRestorePath.Enabled = False @@ -1034,6 +1084,7 @@ Public Class frmGameManager btnBackup.Enabled = False btnMarkAsRestored.Enabled = False btnRestore.Enabled = False + btnImportBackup.Enabled = False btnDeleteBackup.Enabled = False btnOpenBackupFile.Enabled = False btnOpenRestorePath.Enabled = False @@ -1062,6 +1113,7 @@ Public Class frmGameManager lblTags.Visible = True btnImport.Enabled = True btnExport.Enabled = True + btnImportBackup.Enabled = True Case eModes.Disabled grpFilter.Enabled = True lstGames.Enabled = True @@ -1500,6 +1552,38 @@ Public Class frmGameManager End If End Sub + Private Sub TriggerSelectedImportBackup() + Dim sDefaultFolder As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + Dim oBackup As New mgrBackup + Dim sFile As String + Dim sFiles As String() + + 'Show one time warning + If Not (oSettings.SuppressMessages And mgrSettings.eSuppressMessages.BackupImport) = mgrSettings.eSuppressMessages.BackupImport Then + mgrCommon.ShowMessage(frmGameManager_WarningImportBackup, MsgBoxStyle.Information) + oSettings.SuppressMessages = oSettings.SetMessageField(oSettings.SuppressMessages, mgrSettings.eSuppressMessages.BackupImport) + oSettings.SaveSettings() + End If + + ImportBackupList.Clear() + + sFiles = mgrCommon.OpenMultiFileBrowser("GM_ImportBackup", frmGameManager_Choose7zImport, "7z", + frmGameManager_7zBackup, sDefaultFolder, True) + + If sFiles.Length > 0 Then + For Each sFile In sFiles + If Not ImportBackupList.Contains(sFile) Then + ImportBackupList.Add(sFile, oCurrentGame) + End If + Next + + If mgrCommon.ShowMessage(frmGameManager_ConfirmBackupImport, oCurrentGame.CroppedName, MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then + Me.TriggerImportBackup = True + Me.Close() + End If + End If + End Sub + Private Sub TriggerSelectedBackup(Optional ByVal bPrompt As Boolean = True) Dim oData As KeyValuePair(Of String, String) Dim sMsg As String = String.Empty @@ -1620,17 +1704,9 @@ Public Class frmGameManager End Sub - Private Sub ImportOfficialGameList() - Dim sImportURL As String - - If mgrCommon.IsUnix Then - sImportURL = App_URLImportLinux - Else - sImportURL = App_URLImport - End If - + Private Sub ImportOfficialGameList(ByVal sImportUrl As String, ByVal bWinConfigsInLinux As Boolean) If mgrCommon.ShowMessage(frmGameManager_ConfirmOfficialImport, MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then - If mgrMonitorList.DoImport(sImportURL, True, Settings) Then + If mgrMonitorList.DoImport(sImportUrl, True, Settings,, bWinConfigsInLinux) Then mgrMonitorList.SyncMonitorLists(Settings) LoadData() LoadBackupData() @@ -1690,6 +1766,8 @@ Public Class frmGameManager btnDelete.Text = frmGameManager_btnDelete btnAdd.Text = frmGameManager_btnAdd cmsOfficial.Text = frmGameManager_cmsOfficial + cmsOfficialLinux.Text = frmGameManager_cmsOfficialLinux + cmsOfficialWindows.Text = frmGameManager_cmsOfficialWindows cmsFile.Text = frmGameManager_cmsFile lblQuickFilter.Text = frmGameManager_lblQuickFilter lblLimit.Text = frmGameManager_lblLimit @@ -1698,6 +1776,17 @@ Public Class frmGameManager lblComments.Text = frmGameManager_lblComments chkRegEx.Text = frmGameManager_chkRegEx btnGameID.Text = frmGameManager_btnGameID + btnImportBackup.Text = frmGameManager_btnImportBackup + btnProcesses.Text = frmGameManager_btnProcesses + + 'Init Official Import Menu + If mgrCommon.IsUnix Then + cmsOfficial.Text = cmsOfficial.Text.TrimEnd(".") + RemoveHandler cmsOfficial.Click, AddressOf cmsOfficialWindows_Click + Else + cmsOfficialLinux.Visible = False + cmsOfficialWindows.Visible = False + End If 'Init Filter Timer tmFilterTimer = New Timer() @@ -1851,6 +1940,14 @@ Public Class frmGameManager UpdateBuilderButtonLabel(txtExclude.Text, frmGameManager_ExcludeShortcut, btnExclude, (sExclude <> txtExclude.Text)) End Sub + Private Sub btnImportBackup_Click(sender As Object, e As EventArgs) Handles btnImportBackup.Click + If CurrentGame.AppendTimeStamp Then + TriggerSelectedImportBackup() + Else + mgrCommon.ShowMessage(frmGameManager_WarningImportBackupSaveMulti, MsgBoxStyle.Information) + End If + End Sub + Private Sub chkFolderSave_CheckedChanged(sender As Object, e As EventArgs) Handles chkFolderSave.CheckedChanged FolderSaveModeChange() End Sub @@ -1873,8 +1970,23 @@ Public Class frmGameManager cmsImport.Show(btnImport, New Drawing.Point(btnImport.Size.Width - Math.Floor(btnImport.Size.Width * 0.1), btnImport.Size.Height - Math.Floor(btnImport.Size.Height * 0.5)), ToolStripDropDownDirection.AboveRight) End Sub - Private Sub cmsOfficial_Click(sender As Object, e As EventArgs) Handles cmsOfficial.Click - ImportOfficialGameList() + Private Sub cmsOfficialWindows_Click(sender As Object, e As EventArgs) Handles cmsOfficialWindows.Click, cmsOfficial.Click + Dim bWinConfigsinLinux As Boolean = False + 'Show one time warning about Windows configs in Linux + If mgrCommon.IsUnix Then + If Not (oSettings.SuppressMessages And mgrSettings.eSuppressMessages.WinConfigsInLinux) = mgrSettings.eSuppressMessages.WinConfigsInLinux Then + mgrCommon.ShowMessage(frmGameManager_WarningWinConfigsInLinux, MsgBoxStyle.Information) + oSettings.SuppressMessages = oSettings.SetMessageField(oSettings.SuppressMessages, mgrSettings.eSuppressMessages.WinConfigsInLinux) + oSettings.SaveSettings() + End If + bWinConfigsinLinux = True + End If + + ImportOfficialGameList(App_URLImport, bWinConfigsinLinux) + End Sub + + Private Sub cmsOfficialLinux_Click(sender As Object, e As EventArgs) Handles cmsOfficialLinux.Click + ImportOfficialGameList(App_URLImportLinux, False) End Sub Private Sub cmsFile_Click(sender As Object, e As EventArgs) Handles cmsFile.Click @@ -1904,6 +2016,7 @@ Public Class frmGameManager End Sub Private Sub txtSavePath_TextChanged(sender As Object, e As EventArgs) Handles txtSavePath.TextChanged + ttFullPath.RemoveAll() VerifyCleanFolder() End Sub @@ -1922,5 +2035,4 @@ Public Class frmGameManager Private Sub chkMonitorOnly_CheckedChanged(sender As Object, e As EventArgs) Handles chkMonitorOnly.CheckedChanged MonitorOnlyModeChange() End Sub - End Class diff --git a/GBM/Forms/frmMain.vb b/GBM/Forms/frmMain.vb index eb4c039..ca0be18 100644 --- a/GBM/Forms/frmMain.vb +++ b/GBM/Forms/frmMain.vb @@ -228,7 +228,7 @@ Public Class frmMain If oGame.AbsolutePath = False Then If oGame.ProcessPath = String.Empty Then If mgrCommon.IsProcessNotSearchable(oGame) Then bNoAuto = True - oGame.ProcessPath = mgrPath.ProcessPathSearch(oGame.Name, oGame.TrueProcess, mgrCommon.FormatString(frmMain_ErrorRelativePath, oGame.Name), bNoAuto) + oGame.ProcessPath = mgrPath.ProcessPathSearch(oGame.Name, oGame.ProcessName, mgrCommon.FormatString(frmMain_ErrorRelativePath, oGame.Name), bNoAuto) End If If oGame.ProcessPath <> String.Empty Then @@ -258,6 +258,12 @@ Public Class frmMain End Sub + Private Sub RunImportBackup(ByVal oImportBackupList As Hashtable) + PauseScan() + oBackup.ImportBackupFiles(oImportBackupList) + ResumeScan() + End Sub + Private Function DoMultiGameCheck() As Boolean Dim oResult As DialogResult @@ -266,14 +272,9 @@ Public Class frmMain frm.Process = oProcess oResult = frm.ShowDialog() If oResult = DialogResult.OK Then - Dim sProcessPath As String 'Reload settings LoadGameSettings() - 'Retain the process path from old object - sProcessPath = oProcess.GameInfo.ProcessPath oProcess.GameInfo = frm.Game - 'Set the process path into the new object - oProcess.GameInfo.ProcessPath = sProcessPath 'A game was set, return and continue Return True Else @@ -894,6 +895,11 @@ Public Class frmMain If frm.TriggerRestore Then RunRestore(frm.RestoreList) End If + + 'Handle import backup trigger + If frm.TriggerImportBackup Then + RunImportBackup(frm.ImportBackupList) + End If End Sub Private Sub OpenSettings() @@ -940,7 +946,7 @@ Public Class frmMain Dim frm As New frmVariableManager PauseScan() frm.ShowDialog() - mgrPath.CustomVariablesReload() + mgrPath.LoadCustomVariables() mgrMonitorList.SyncMonitorLists(oSettings) ResumeScan() End Sub @@ -1689,15 +1695,6 @@ Public Class frmMain End Function - Private Sub CheckForSavedDuplicate() - For Each o As clsGame In oProcess.DuplicateList - If o.ProcessPath.ToLower = oProcess.GameInfo.ProcessPath.ToLower Then - oProcess.GameInfo = o - oProcess.Duplicate = False - End If - Next - End Sub - Private Function CheckForSavedPath() As Boolean If oProcess.GameInfo.ProcessPath <> String.Empty Then Return True @@ -1894,11 +1891,12 @@ Public Class frmMain Private Sub ScanTimerEventProcessor(myObject As Object, ByVal myEventArgs As EventArgs) Handles tmScanTimer.Tick Dim bNeedsPath As Boolean = False + Dim bWineProcess As Boolean = False Dim bContinue As Boolean = True Dim iErrorCode As Integer = 0 Dim sErrorMessage As String = String.Empty - If oProcess.SearchRunningProcesses(hshScanList, bNeedsPath, iErrorCode, bProcessDebugMode) Then + If oProcess.SearchRunningProcesses(hshScanList, bNeedsPath, bWineProcess, iErrorCode, bProcessDebugMode) Then PauseScan(True) If bNeedsPath Then @@ -1928,8 +1926,24 @@ Public Class frmMain End If End If + If bWineProcess Then + 'Attempt a path conversion if the game configuration is using an absolute windows path that we can convert + If mgrVariables.CheckForReservedVariables(oProcess.GameInfo.TruePath) Then + Dim sWinePrefix As String = mgrPath.GetWinePrefix(oProcess.FoundProcess) + Dim sWineSavePath As String + If Not sWinePrefix = String.Empty Then + UpdateLog(mgrCommon.FormatString(frmMain_WinePrefix, New String() {oProcess.GameInfo.Name, sWinePrefix}), False) + sWineSavePath = mgrPath.GetWineSavePath(sWinePrefix, oProcess.GameInfo.TruePath) + If Not sWineSavePath = oProcess.GameInfo.TruePath Then + oProcess.GameInfo.TruePath = sWineSavePath + oProcess.GameInfo.AbsolutePath = True + UpdateLog(mgrCommon.FormatString(frmMain_WineSavePath, New String() {oProcess.GameInfo.Name, sWineSavePath}), False) + End If + End If + End If + End If + If bContinue = True Then - CheckForSavedDuplicate() If oProcess.Duplicate Then UpdateLog(frmMain_MultipleGamesDetected, oSettings.ShowDetectionToolTips) UpdateStatus(frmMain_MultipleGamesDetected) diff --git a/GBM/Forms/frmProcessManager.vb b/GBM/Forms/frmProcessManager.vb index cc56904..60a6d75 100644 --- a/GBM/Forms/frmProcessManager.vb +++ b/GBM/Forms/frmProcessManager.vb @@ -56,7 +56,7 @@ Public Class frmProcessManager End If sNewPath = mgrCommon.OpenFileBrowser("PM_Process", frmProcessManager_ChooseProcess, "exe", - frmProcessManager_Executable, sDefaultFolder, False, True) + frmProcessManager_Executable, sDefaultFolder, True) If sNewPath <> String.Empty Then txtPath.Text = sNewPath diff --git a/GBM/Forms/frmSettings.Designer.vb b/GBM/Forms/frmSettings.Designer.vb index 3f9a80a..1b21cb5 100644 --- a/GBM/Forms/frmSettings.Designer.vb +++ b/GBM/Forms/frmSettings.Designer.vb @@ -64,6 +64,7 @@ Partial Class frmSettings Me.grpLogOptions = New System.Windows.Forms.GroupBox() Me.chkDisableSyncMessages = New System.Windows.Forms.CheckBox() Me.grpGameData = New System.Windows.Forms.GroupBox() + Me.chkShowResolvedPaths = New System.Windows.Forms.CheckBox() Me.chkSessionTracking = New System.Windows.Forms.CheckBox() Me.lstSettings = New System.Windows.Forms.ListBox() Me.btnResetMessages = New System.Windows.Forms.Button() @@ -102,7 +103,7 @@ Partial Class frmSettings ' 'btnOptionalFields ' - Me.btnOptionalFields.Location = New System.Drawing.Point(6, 65) + Me.btnOptionalFields.Location = New System.Drawing.Point(6, 88) Me.btnOptionalFields.Name = "btnOptionalFields" Me.btnOptionalFields.Size = New System.Drawing.Size(216, 23) Me.btnOptionalFields.TabIndex = 3 @@ -443,7 +444,7 @@ Partial Class frmSettings Me.grpGameMonitoringOptions.Controls.Add(Me.lblMinutes) Me.grpGameMonitoringOptions.Controls.Add(Me.chkShowDetectionTips) Me.grpGameMonitoringOptions.Controls.Add(Me.nudSuppressBackupThreshold) - Me.grpGameMonitoringOptions.Location = New System.Drawing.Point(6, 115) + Me.grpGameMonitoringOptions.Location = New System.Drawing.Point(6, 141) Me.grpGameMonitoringOptions.Name = "grpGameMonitoringOptions" Me.grpGameMonitoringOptions.Size = New System.Drawing.Size(354, 65) Me.grpGameMonitoringOptions.TabIndex = 1 @@ -481,7 +482,7 @@ Partial Class frmSettings ' Me.grpLogOptions.Controls.Add(Me.chkDisableSyncMessages) Me.grpLogOptions.Controls.Add(Me.chkAutoSaveLog) - Me.grpLogOptions.Location = New System.Drawing.Point(6, 186) + Me.grpLogOptions.Location = New System.Drawing.Point(6, 212) Me.grpLogOptions.Name = "grpLogOptions" Me.grpLogOptions.Size = New System.Drawing.Size(354, 65) Me.grpLogOptions.TabIndex = 2 @@ -500,16 +501,27 @@ Partial Class frmSettings ' 'grpGameData ' + Me.grpGameData.Controls.Add(Me.chkShowResolvedPaths) Me.grpGameData.Controls.Add(Me.chkSessionTracking) Me.grpGameData.Controls.Add(Me.chkTimeTracking) Me.grpGameData.Controls.Add(Me.btnOptionalFields) Me.grpGameData.Location = New System.Drawing.Point(6, 12) Me.grpGameData.Name = "grpGameData" - Me.grpGameData.Size = New System.Drawing.Size(354, 97) + Me.grpGameData.Size = New System.Drawing.Size(354, 123) Me.grpGameData.TabIndex = 0 Me.grpGameData.TabStop = False Me.grpGameData.Text = "Game Data Options" ' + 'chkShowResolvedPaths + ' + Me.chkShowResolvedPaths.AutoSize = True + Me.chkShowResolvedPaths.Location = New System.Drawing.Point(6, 65) + Me.chkShowResolvedPaths.Name = "chkShowResolvedPaths" + Me.chkShowResolvedPaths.Size = New System.Drawing.Size(154, 17) + Me.chkShowResolvedPaths.TabIndex = 2 + Me.chkShowResolvedPaths.Text = "Show resolved save paths " + Me.chkShowResolvedPaths.UseVisualStyleBackColor = True + ' 'chkSessionTracking ' Me.chkSessionTracking.AutoSize = True @@ -698,4 +710,5 @@ Partial Class frmSettings Friend WithEvents chkStartToTray As CheckBox Friend WithEvents chkMonitorOnStartup As CheckBox Friend WithEvents grpGameMonitoringOptions As GroupBox + Friend WithEvents chkShowResolvedPaths As CheckBox End Class diff --git a/GBM/Forms/frmSettings.vb b/GBM/Forms/frmSettings.vb index 59584ae..49a0443 100644 --- a/GBM/Forms/frmSettings.vb +++ b/GBM/Forms/frmSettings.vb @@ -60,6 +60,7 @@ Public Class frmSettings oSettings.AutoMark = chkAutoMark.Checked oSettings.TimeTracking = chkTimeTracking.Checked oSettings.SessionTracking = chkSessionTracking.Checked + oSettings.ShowResolvedPaths = chkShowResolvedPaths.Checked oSettings.SuppressBackup = chkSuppressBackup.Checked oSettings.SuppressBackupThreshold = nudSuppressBackupThreshold.Value oSettings.CompressionLevel = cboCompression.SelectedValue @@ -189,6 +190,7 @@ Public Class frmSettings txtBackupFolder.Text = oSettings.BackupFolder chkTimeTracking.Checked = oSettings.TimeTracking chkSessionTracking.Checked = oSettings.SessionTracking + chkShowResolvedPaths.Checked = oSettings.ShowResolvedPaths chkSuppressBackup.Checked = oSettings.SuppressBackup nudSuppressBackupThreshold.Value = oSettings.SuppressBackupThreshold nudSuppressBackupThreshold.Enabled = chkSuppressBackup.Checked @@ -322,6 +324,7 @@ Public Class frmSettings grpLogOptions.Text = frmSettings_grpLogOptions chkDisableSyncMessages.Text = frmSettings_chkDisableSyncMessages grpGameMonitoringOptions.Text = frmSettings_grpGameMonitoringOptions + chkShowResolvedPaths.Text = frmSettings_chkShowResolvedPaths 'Unix Handler If mgrCommon.IsUnix Then diff --git a/GBM/Forms/frmVariableManager.vb b/GBM/Forms/frmVariableManager.vb index da90901..a3d18d9 100644 --- a/GBM/Forms/frmVariableManager.vb +++ b/GBM/Forms/frmVariableManager.vb @@ -289,6 +289,12 @@ Public Class frmVariableManager Return False End If + If mgrVariables.GetReservedVariables.Contains(txtName.Text.ToUpper) Then + mgrCommon.ShowMessage(frmVariableManager_ErrorVariableReserved, txtName.Text, MsgBoxStyle.Exclamation) + txtName.Focus() + Return False + End If + Return True End Function diff --git a/GBM/Managers/mgrBackup.vb b/GBM/Managers/mgrBackup.vb index 1163fcf..8d3eb30 100644 --- a/GBM/Managers/mgrBackup.vb +++ b/GBM/Managers/mgrBackup.vb @@ -42,7 +42,7 @@ Public Class mgrBackup 'Create manifest item oItem.MonitorID = oGameInfo.ID 'Keep the path relative to the manifest location - oItem.FileName = sBackupFile.Replace(Path.GetDirectoryName(mgrPath.RemoteDatabaseLocation) & Path.DirectorySeparatorChar, "") + oItem.FileName = sBackupFile.Replace(Settings.BackupFolder & Path.DirectorySeparatorChar, String.Empty) oItem.DateUpdated = dTimeStamp oItem.UpdatedBy = My.Computer.Name oItem.CheckSum = sCheckSum @@ -196,6 +196,60 @@ Public Class mgrBackup End If End Sub + Private Function BuildFileTimeStamp(ByVal dDate As Date) As String + Return " " & dDate.Month & "-" & dDate.Day & "-" & dDate.Year & "-" & dDate.Hour & "-" & dDate.Minute & "-" & dDate.Second + End Function + + Private Function HandleSubFolder(ByVal oGame As clsGame, ByVal sPath As String) As Boolean + Try + If Not Directory.Exists(sPath) Then + Directory.CreateDirectory(sPath) + End If + Catch ex As Exception + RaiseEvent UpdateLog(mgrCommon.FormatString(mgrBackup_ErrorSubFolderCreate, New String() {oGame.Name, ex.Message}), False, ToolTipIcon.Error, True) + Return False + End Try + + Return True + End Function + + Public Sub ImportBackupFiles(ByVal hshImportList As Hashtable) + Dim oGame As clsGame + Dim bContinue As Boolean = True + Dim sFileToImport As String + Dim sBackupFile As String + Dim oBackup As clsBackup + + For Each de As DictionaryEntry In hshImportList + sFileToImport = CStr(de.Key) + oGame = DirectCast(de.Value, clsGame) + If File.Exists(sFileToImport) Then + + sBackupFile = oSettings.BackupFolder + If oSettings.CreateSubFolder Then + sBackupFile = sBackupFile & Path.DirectorySeparatorChar & GetFileName(oGame) + bContinue = HandleSubFolder(oGame, sBackupFile) + End If + + If bContinue Then + oBackup = New clsBackup + oBackup.MonitorID = oGame.ID + oBackup.DateUpdated = File.GetLastWriteTime(sFileToImport) + oBackup.UpdatedBy = mgrBackup_ImportedFile + sBackupFile = sBackupFile & Path.DirectorySeparatorChar & GetFileName(oGame) & BuildFileTimeStamp(oBackup.DateUpdated) & ".7z" + oBackup.FileName = sBackupFile.Replace(Settings.BackupFolder & Path.DirectorySeparatorChar, String.Empty) + If mgrCommon.CopyFile(sFileToImport, sBackupFile, False) Then + oBackup.CheckSum = mgrHash.Generate_SHA256_Hash(sBackupFile) + mgrManifest.DoManifestAdd(oBackup, mgrSQLite.Database.Remote) + RaiseEvent UpdateLog(mgrCommon.FormatString(mgrBackup_ImportSuccess, New String() {sFileToImport, oGame.Name}), False, ToolTipIcon.Error, True) + Else + RaiseEvent UpdateLog(mgrCommon.FormatString(mgrBackup_ErrorImportBackupCopy, sFileToImport), False, ToolTipIcon.Error, True) + End If + End If + End If + Next + End Sub + Public Sub DoBackup(ByVal oBackupList As List(Of clsGame)) Dim oGame As clsGame Dim bDoBackup As Boolean @@ -213,7 +267,7 @@ Public Class mgrBackup sBackupFile = oSettings.BackupFolder sSavePath = String.Empty dTimeStamp = Date.Now - sTimeStamp = " " & dTimeStamp.Month & "-" & dTimeStamp.Day & "-" & dTimeStamp.Year & "-" & dTimeStamp.Hour & "-" & dTimeStamp.Minute & "-" & dTimeStamp.Second + sTimeStamp = BuildFileTimeStamp(dTimeStamp) sHash = String.Empty bDoBackup = True bBackupCompleted = False @@ -222,14 +276,7 @@ Public Class mgrBackup If oSettings.CreateSubFolder Then sBackupFile = sBackupFile & Path.DirectorySeparatorChar & GetFileName(oGame) - Try - If Not Directory.Exists(sBackupFile) Then - Directory.CreateDirectory(sBackupFile) - End If - Catch ex As Exception - RaiseEvent UpdateLog(mgrCommon.FormatString(mgrBackup_ErrorSubFolderCreate, New String() {oGame.Name, ex.Message}), False, ToolTipIcon.Error, True) - bDoBackup = False - End Try + bDoBackup = HandleSubFolder(oGame, sBackupFile) End If If oGame.AppendTimeStamp Then diff --git a/GBM/Managers/mgrCommon.vb b/GBM/Managers/mgrCommon.vb index 07379bc..e49592d 100644 --- a/GBM/Managers/mgrCommon.vb +++ b/GBM/Managers/mgrCommon.vb @@ -144,9 +144,9 @@ Public Class mgrCommon Return String.Empty End Function - Public Shared Function OpenFileBrowser(ByVal sName As String, ByVal sTitle As String, ByVal sExtension As String, ByVal sFileType As String, ByVal sDefaultFolder As String, - ByVal bMulti As Boolean, Optional ByVal bSavedPath As Boolean = True) As String - Dim fbBrowser As New OpenFileDialog + Private Shared Function BuildFileBrowser(ByVal sName As String, ByVal sTitle As String, ByVal sExtension As String, ByVal sFileType As String, ByVal sDefaultFolder As String, + ByVal bMulti As Boolean, ByRef fbBrowser As OpenFileDialog, Optional ByVal bSavedPath As Boolean = True) As Boolean + Dim oSavedPath As New clsSavedPath fbBrowser.Title = sTitle @@ -171,21 +171,40 @@ Public Class mgrCommon mgrSavedPath.AddUpdatePath(oSavedPath) End If - If bMulti Then - Dim sFileNames As String = String.Empty - For Each sFileName As String In fbBrowser.FileNames - sFileNames &= sFileName & "|" - Next - sFileNames = sFileNames.TrimEnd("|") - Return sFileNames - Else - Return fbBrowser.FileName - End If + Return True + End If + + Return False + End Function + + Public Shared Function OpenFileBrowser(ByVal sName As String, ByVal sTitle As String, ByVal sExtension As String, ByVal sFileType As String, ByVal sDefaultFolder As String, + Optional ByVal bSavedPath As Boolean = True) As String + Dim fbBrowser As New OpenFileDialog + Dim bResult As Boolean + + bResult = BuildFileBrowser(sName, sTitle, sExtension, sFileType, sDefaultFolder, False, fbBrowser, bSavedPath) + + If bResult Then + Return fbBrowser.FileName End If Return String.Empty End Function + Public Shared Function OpenMultiFileBrowser(ByVal sName As String, ByVal sTitle As String, ByVal sExtension As String, ByVal sFileType As String, ByVal sDefaultFolder As String, + Optional ByVal bSavedPath As Boolean = True) As String() + Dim fbBrowser As New OpenFileDialog + Dim bResult As Boolean + + bResult = BuildFileBrowser(sName, sTitle, sExtension, sFileType, sDefaultFolder, True, fbBrowser, bSavedPath) + + If bResult Then + Return fbBrowser.FileNames + End If + + Return New String() {} + End Function + Public Shared Function OpenFolderBrowser(ByVal sName As String, ByVal sTitle As String, ByVal sDefaultFolder As String, ByVal bEnableNewFolder As Boolean, Optional ByVal bSavedPath As Boolean = True) As String Dim fbBrowser As New FolderBrowserDialog @@ -225,7 +244,7 @@ Public Class mgrCommon If oGame.ProcessName.ToLower.Contains(s) Then bFound = True Next - If bFound Or oGame.Duplicate = True Then + If bFound Then Return True Else Return False @@ -415,6 +434,21 @@ Public Class mgrCommon End If End Function + 'Copy a file + Public Shared Function CopyFile(ByVal sSourcePath As String, ByVal sDestinationPath As String, ByVal bOverWrite As Boolean) As Boolean + Try + If File.Exists(sSourcePath) Then + File.Copy(sSourcePath, sDestinationPath, bOverWrite) + Else + Return False + End If + Catch + Return False + End Try + + Return True + End Function + 'Delete file based on OS type Public Shared Sub DeleteFile(ByVal sPath As String, Optional ByVal bRecycle As Boolean = True) If File.Exists(sPath) Then diff --git a/GBM/Managers/mgrMonitorList.vb b/GBM/Managers/mgrMonitorList.vb index 8141ab9..d66f33e 100644 --- a/GBM/Managers/mgrMonitorList.vb +++ b/GBM/Managers/mgrMonitorList.vb @@ -45,7 +45,7 @@ Public Class mgrMonitorList hshParams.Add("ID", oGame.ID) hshParams.Add("Name", oGame.Name) - hshParams.Add("Process", oGame.TrueProcess) + hshParams.Add("Process", oGame.ProcessName) hshParams.Add("Path", oGame.TruePath) hshParams.Add("AbsolutePath", oGame.AbsolutePath) hshParams.Add("FolderSave", oGame.FolderSave) @@ -75,10 +75,8 @@ Public Class mgrMonitorList Dim hshList As New Hashtable Dim hshDupeList As New Hashtable Dim oGame As clsGame - Dim oCompareGame As clsGame - Dim bIsDupe As Boolean - sSQL = "Select * FROM monitorlist ORDER BY Name Asc" + sSQL = "SELECT * FROM monitorlist ORDER BY Name ASC" oData = oDatabase.ReadParamData(sSQL, New Hashtable) For Each dr As DataRow In oData.Tables(0).Rows @@ -87,39 +85,7 @@ Public Class mgrMonitorList Case eListTypes.FullList hshList.Add(oGame.ID, oGame) Case eListTypes.ScanList - For Each de As DictionaryEntry In hshList - bIsDupe = False - oCompareGame = DirectCast(de.Value, clsGame) - - If oCompareGame.IsRegEx Then - If oGame.IsRegEx Then - If oCompareGame.ProcessName = oGame.ProcessName Then - bIsDupe = True - End If - Else - If Regex.IsMatch(oGame.ProcessName, oCompareGame.ProcessName) Then - bIsDupe = True - End If - End If - Else - If oGame.IsRegEx Then - If Regex.IsMatch(oCompareGame.ProcessName, oGame.ProcessName) Then - bIsDupe = True - End If - Else - If oGame.ProcessName = oCompareGame.ProcessName Then - bIsDupe = True - End If - End If - End If - - If bIsDupe Then - DirectCast(hshList.Item(oCompareGame.ProcessName), clsGame).Duplicate = True - oGame.ProcessName = oGame.CompoundKey - oGame.Duplicate = True - End If - Next - If oGame.Enabled Then hshList.Add(oGame.ProcessName, oGame) + If oGame.Enabled Then hshList.Add(oGame.ID, oGame) End Select Next @@ -413,7 +379,7 @@ Public Class mgrMonitorList 'Core Parameters hshParams.Add("ID", oGame.ID) hshParams.Add("Name", oGame.Name) - hshParams.Add("Process", oGame.TrueProcess) + hshParams.Add("Process", oGame.ProcessName) hshParams.Add("Path", oGame.TruePath) hshParams.Add("AbsolutePath", oGame.AbsolutePath) hshParams.Add("FolderSave", oGame.FolderSave) @@ -822,13 +788,13 @@ Public Class mgrMonitorList Return True End Function - Public Shared Function DoImport(ByVal sPath As String, ByVal bOfficial As Boolean, ByRef oSettings As mgrSettings, Optional ByVal bStartUpWizard As Boolean = False) As Boolean + Public Shared Function DoImport(ByVal sPath As String, ByVal bOfficial As Boolean, ByRef oSettings As mgrSettings, Optional ByVal bStartUpWizard As Boolean = False, Optional ByVal bWinConfigsInLinux As Boolean = False) As Boolean If mgrCommon.IsAddress(sPath) Then If mgrCommon.CheckAddress(sPath) Then If bOfficial And Not bStartUpWizard And Not ((oSettings.SuppressMessages And mgrSettings.eSuppressMessages.GameIDSync) = mgrSettings.eSuppressMessages.GameIDSync) Then SyncGameIDs(sPath, oSettings, True) End If - ImportMonitorList(sPath, True) + ImportMonitorList(sPath, True, bWinConfigsInLinux) Return True Else mgrCommon.ShowMessage(mgrMonitorList_WebNoReponse, sPath, MsgBoxStyle.Exclamation) @@ -836,7 +802,7 @@ Public Class mgrMonitorList End If Else If File.Exists(sPath) Then - ImportMonitorList(sPath) + ImportMonitorList(sPath,, bWinConfigsInLinux) Return True Else mgrCommon.ShowMessage(mgrMonitorList_FileNotFound, sPath, MsgBoxStyle.Exclamation) @@ -846,7 +812,7 @@ Public Class mgrMonitorList Return True End Function - Private Shared Sub ImportMonitorList(ByVal sLocation As String, Optional ByVal bWebRead As Boolean = False) + Private Shared Sub ImportMonitorList(ByVal sLocation As String, Optional ByVal bWebRead As Boolean = False, Optional ByVal bWinConfigsInLinux As Boolean = False) Dim hshCompareFrom As New Hashtable Dim hshCompareTo As Hashtable Dim hshSyncItems As Hashtable @@ -860,7 +826,7 @@ Public Class mgrMonitorList Exit Sub End If - If oExportInfo.AppVer < 110 Then + If oExportInfo.AppVer < 115 Then If mgrCommon.ShowMessage(mgrMonitorList_ImportVersionWarning, MsgBoxStyle.YesNo) = MsgBoxResult.No Then Exit Sub End If @@ -893,6 +859,7 @@ Public Class mgrMonitorList Dim frm As New frmAdvancedImport frm.ImportInfo = oExportInfo frm.ImportData = hshSyncItems + frm.ModWinConfigsForLinux = bWinConfigsInLinux If frm.ShowDialog() = DialogResult.OK Then Cursor.Current = Cursors.WaitCursor diff --git a/GBM/Managers/mgrPath.vb b/GBM/Managers/mgrPath.vb index 340ab22..d6e03e9 100644 --- a/GBM/Managers/mgrPath.vb +++ b/GBM/Managers/mgrPath.vb @@ -15,7 +15,8 @@ Public Class mgrPath Private Shared oReleaseType As ProcessorArchitecture = AssemblyName.GetAssemblyName(Application.ExecutablePath()).ProcessorArchitecture Shared Sub New() - hshCustomVariables = mgrVariables.ReadVariables + SetEnv() + LoadCustomVariables() End Sub Shared ReadOnly Property ReleaseType As Integer @@ -202,6 +203,111 @@ Public Class mgrPath Return sResult End Function + Private Shared Function BuildWinePath(ByVal sPath As String, ByVal sWinePrefix As String) As String + Dim sRealPath As String + Dim cDriveLetter As Char + Dim sWineDrive As String + + Try + 'Grab Path + sRealPath = sPath.Split("=")(1) + + 'Remove Quotes + sRealPath = sRealPath.TrimStart("""") + sRealPath = sRealPath.TrimEnd("""") + + 'Flip Seperators + sRealPath = sRealPath.Replace("\\", "/") + + 'Change Wine Drive + cDriveLetter = sRealPath.Chars(sRealPath.IndexOf(":") - 1) + sWineDrive = "drive_" & cDriveLetter + sRealPath = sRealPath.Replace(cDriveLetter & ":", sWineDrive.ToLower) + + Return sWinePrefix & Path.DirectorySeparatorChar & sRealPath + Catch ex As Exception + mgrCommon.ShowMessage(mgrPath_ErrorBuildingWinePath, ex.Message, MsgBoxStyle.Exclamation) + Return String.Empty + End Try + End Function + + Public Shared Function GetWineSavePath(ByVal sPrefix As String, ByVal sPath As String) As String + Dim sRegistry As String + Dim sWinePath As String + Dim sReplace As String + Dim oParse As Regex + Dim oMatch As Match + + Try + If sPath.Contains("%APPDATA%") Then + sReplace = "%APPDATA%" + sRegistry = File.ReadAllText(sPrefix & Path.DirectorySeparatorChar & "user.reg") + oParse = New Regex("""AppData""="".+?(?=\n)") + ElseIf sPath.Contains("%LOCALAPPDATA%Low") Then + sReplace = "%LOCALAPPDATA%Low" + sRegistry = File.ReadAllText(sPrefix & Path.DirectorySeparatorChar & "user.reg") + oParse = New Regex("""{A520A1A4-1780-4FF6-BD18-167343C5AF16}""="".+?(?=\n)") + ElseIf sPath.Contains("%LOCALAPPDATA%") Then + sReplace = "%LOCALAPPDATA%" + sRegistry = File.ReadAllText(sPrefix & Path.DirectorySeparatorChar & "user.reg") + oParse = New Regex("""Local AppData""="".+?(?=\n)") + ElseIf sPath.Contains("%USERDOCUMENTS%") Then + sReplace = "%USERDOCUMENTS%" + sRegistry = File.ReadAllText(sPrefix & Path.DirectorySeparatorChar & "user.reg") + oParse = New Regex("""Personal""="".+?(?=\n)") + ElseIf sPath.Contains("%COMMONDOCUMENTS%") Then + sReplace = "%COMMONDOCUMENTS%" + sRegistry = File.ReadAllText(sPrefix & Path.DirectorySeparatorChar & "system.reg") + oParse = New Regex("""Common Documents""="".+?(?=\n)") + ElseIf sPath.Contains("%USERPROFILE%") Then + sReplace = "%USERPROFILE%" + sRegistry = File.ReadAllText(sPrefix & Path.DirectorySeparatorChar & "user.reg") + oParse = New Regex("""Desktop""="".+?(?=\\\\Desktop)") + Else + Return sPath + End If + + If oParse.IsMatch(sRegistry) Then + oMatch = oParse.Match(sRegistry) + sWinePath = BuildWinePath(oMatch.Value, sPrefix) + sPath = sPath.Replace("\", Path.DirectorySeparatorChar) + Return sPath.Replace(sReplace, sWinePath) + End If + + Return sPath + Catch ex As Exception + mgrCommon.ShowMessage(mgrPath_ErrorConvertWineSavePath, ex.Message, MsgBoxStyle.Exclamation) + Return sPath + End Try + End Function + + Public Shared Function GetWinePrefix(ByVal prs As Process) As String + Dim prps As Process + Dim sPsinfo As String + Dim oParse As New Regex("WINEPREFIX=.+?(?= )") + Dim oMatch As Match + + Try + prps = New Process + prps.StartInfo.FileName = "/bin/ps" + prps.StartInfo.Arguments = "e " & prs.Id.ToString + prps.StartInfo.UseShellExecute = False + prps.StartInfo.RedirectStandardOutput = True + prps.StartInfo.CreateNoWindow = True + prps.Start() + sPsinfo = prps.StandardOutput.ReadToEnd() + If oParse.IsMatch(sPsinfo) Then + oMatch = oParse.Match(sPsinfo) + Return oMatch.Value.Trim("/").Split("=")(1) + Else + Return String.Empty + End If + Catch ex As Exception + mgrCommon.ShowMessage(mgrPath_ErrorWinePrefix, ex.Message, MsgBoxStyle.Exclamation) + Return String.Empty + End Try + End Function + Public Shared Function CheckSpecialPaths() As Boolean Dim hshEnvs As New Hashtable Dim bNoError As Boolean = True @@ -224,14 +330,22 @@ Public Class mgrPath Return bNoError End Function - Public Shared Function ReplaceSpecialPaths(sValue As String) As String - Dim sMyDocs As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) - Dim sPublicDocs As String = Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments) - Dim sAppDataRoaming As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) - Dim sAppDataLocal As String = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) - Dim sCurrentUser As String = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) - Dim oCustomVariable As clsPathVariable + Private Shared Sub SetEnv() + If Not mgrCommon.IsUnix Then + Environment.SetEnvironmentVariable("USERDOCUMENTS", Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)) + Environment.SetEnvironmentVariable("COMMONDOCUMENTS", Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments)) + End If + End Sub + + Public Shared Function ReplaceSpecialPaths(ByVal sValue As String) As String + Dim sXdgData As String = "${XDG_DATA_HOME:-~/.local/share}" + Dim sEnvAppDataLocal As String = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + Dim sXdgConfig As String = "${XDG_CONFIG_HOME:-~/.config}" + Dim sEnvAppDataRoaming As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + Dim sHomeDir As String = "${HOME}" + Dim sEnvCurrentUser As String = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + Dim oCustomVariable As clsPathVariable For Each oCustomVariable In hshCustomVariables.Values If sValue.Contains(oCustomVariable.FormattedName) Then @@ -239,68 +353,92 @@ Public Class mgrPath End If Next - If sValue.Contains("*appdatalocal*") Then - Return sValue.Replace("*appdatalocal*", sAppDataLocal) - End If - - If sValue.Contains("*appdataroaming*") Then - Return sValue.Replace("*appdataroaming*", sAppDataRoaming) - End If - - 'This needs to be tested last for Unix compatability - If sValue.Contains("*mydocs*") Then - Return sValue.Replace("*mydocs*", sMyDocs) - End If - - 'Don't use these in Unix - If Not mgrCommon.IsUnix Then - If sValue.Contains("*publicdocs*") Then - Return sValue.Replace("*publicdocs*", sPublicDocs) + If mgrCommon.IsUnix Then + '$VAR_iable + Dim oParse As New Regex("\$([a-zA-Z0-9_]+)") + '${VAR_iable} but not advanced syntax like ${VAR:-iable} + Dim oParseBracketed As New Regex("\$\{([a-zA-Z0-9_]+?)\}") + '~ not inside ${...} + Dim oParseTilde As New Regex("~(?![^\$\{]*\})") + If sEnvCurrentUser = String.Empty Then + 'Fall back + sEnvCurrentUser = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + End If + If sEnvCurrentUser = String.Empty Then + 'Fall back + sEnvCurrentUser = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) End If - If sValue.Contains("*currentuser*") Then - Return sValue.Replace("*currentuser*", sCurrentUser) - End If + '$HOME to ${HOME} + sValue = oParse.Replace(sValue, "${$1}") + 'Special notations for home directory + sValue = oParseTilde.Replace(sValue, "${HOME}") + 'XDG Base Directory Specification has default values + sValue = sValue.Replace("${XDG_DATA_HOME}", sXdgData) + sValue = sValue.Replace("${XDG_CONFIG_HOME}", sXdgConfig) + + 'Replace with paths + sValue = sValue.Replace(sXdgData, sEnvAppDataLocal) + sValue = sValue.Replace(sXdgConfig, sEnvAppDataRoaming) + sValue = sValue.Replace(sHomeDir, sEnvCurrentUser) + + 'Escape real Windows variables + sValue = sValue.Replace("%", "\%") + 'Transform Linux variables to Windows variables + sValue = oParseBracketed.Replace(sValue, "%$1%") + End If + + 'On Linux real Linux environmental variables are used + sValue = Environment.ExpandEnvironmentVariables(sValue) + + If mgrCommon.IsUnix Then + 'Transform missing variables back + Dim oParse As New Regex("%([a-zA-Z0-9_]+?)%") + sValue = oParse.Replace(sValue, "${$1}") + 'Unscape real Windows variables + sValue = sValue.Replace("\%", "%") End If Return sValue End Function Public Shared Function ReverseSpecialPaths(sValue As String) As String - Dim sMyDocs As String = "*mydocs*" + Dim sMyDocs As String = "%USERDOCUMENTS%" Dim sEnvMyDocs As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) - Dim sPublicDocs As String = "*publicdocs*" + Dim sPublicDocs As String = "%COMMONDOCUMENTS%" Dim sEnvPublicDocs As String = Environment.GetFolderPath(Environment.SpecialFolder.CommonDocuments) - Dim sAppDataLocal As String = "*appdatalocal*" + Dim sAppDataLocal As String = "%LOCALAPPDATA%" + Dim sXdgData As String = "${XDG_DATA_HOME:-~/.local/share}" Dim sEnvAppDataLocal As String = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) - Dim sAppDataRoaming As String = "*appdataroaming*" + Dim sAppDataRoaming As String = "%APPDATA%" + Dim sXdgConfig As String = "${XDG_CONFIG_HOME:-~/.config}" Dim sEnvAppDataRoaming As String = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) - Dim sCurrentUser As String = "*currentuser*" + Dim sCurrentUser As String = "%USERPROFILE%" + Dim sHomeDir As String = "~" Dim sEnvCurrentUser As String = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) Dim oCustomVariable As clsPathVariable - For Each oCustomVariable In hshCustomVariables.Values If sValue.Contains(oCustomVariable.Path) Then Return sValue.Replace(oCustomVariable.Path, oCustomVariable.FormattedName) End If Next - If sValue.Contains(sEnvAppDataRoaming) Then - Return sValue.Replace(sEnvAppDataRoaming, sAppDataRoaming) - End If - - If sValue.Contains(sEnvAppDataLocal) Then - Return sValue.Replace(sEnvAppDataLocal, sAppDataLocal) - End If - - 'This needs to be tested last for Unix compatability - If sValue.Contains(sEnvMyDocs) Then - Return sValue.Replace(sEnvMyDocs, sMyDocs) - End If - - 'Don't use these in Unix If Not mgrCommon.IsUnix Then + If sValue.Contains(sEnvAppDataRoaming) Then + Return sValue.Replace(sEnvAppDataRoaming, sAppDataRoaming) + End If + + If sValue.Contains(sEnvAppDataLocal) Then + Return sValue.Replace(sEnvAppDataLocal, sAppDataLocal) + End If + + 'This needs to be tested last for Unix compatability + If sValue.Contains(sEnvMyDocs) Then + Return sValue.Replace(sEnvMyDocs, sMyDocs) + End If + + 'Mono doesn't set a path for these folders If sValue.Contains(sEnvPublicDocs) Then Return sValue.Replace(sEnvPublicDocs, sPublicDocs) End If @@ -308,6 +446,28 @@ Public Class mgrPath If sValue.Contains(sEnvCurrentUser) Then Return sValue.Replace(sEnvCurrentUser, sCurrentUser) End If + Else + 'Use different paths on Linux + If sValue.Contains(sEnvAppDataRoaming) Then + Return sValue.Replace(sEnvAppDataRoaming, sXdgConfig) + End If + + If sValue.Contains(sEnvAppDataLocal) Then + Return sValue.Replace(sEnvAppDataLocal, sXdgData) + End If + + 'Must be last + If sValue.Contains(sEnvCurrentUser) Then + If sEnvCurrentUser = String.Empty Then + 'Fall back + sEnvCurrentUser = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + End If + If sEnvCurrentUser = String.Empty Then + 'Fall back + sEnvCurrentUser = sMyDocs + End If + Return sValue.Replace(sEnvCurrentUser, sHomeDir) + End If End If Return sValue @@ -345,8 +505,10 @@ Public Class mgrPath Public Shared Function VerifyCustomVariables(ByVal hshScanlist As Hashtable, ByRef sGames As String) As Boolean Dim hshCustomVariables As Hashtable = mgrVariables.ReadVariables + 'Reserved variables will be resolved on Windows, but not on a Linux. Therefore we an ignore list here, otherwise GBM will bitch about them when using Windows configurations for Wine. + Dim oReservedVariables As List(Of String) = mgrVariables.GetReservedVariables Dim sVariableCheck As String - Dim sPattern As String = "\*(.*)\*" + Dim sPattern As String = "\%(.*)\%" Dim oGame As clsGame Dim oMatch As Match Dim bClean As Boolean = True @@ -354,8 +516,8 @@ Public Class mgrPath For Each oGame In hshScanlist.Values oMatch = Regex.Match(oGame.Path, sPattern) If oMatch.Success Then - sVariableCheck = oMatch.Value.Replace("*", String.Empty) - If Not hshCustomVariables.ContainsKey(sVariableCheck) Then + sVariableCheck = oMatch.Value.Replace("%", String.Empty) + If Not hshCustomVariables.ContainsKey(sVariableCheck) And Not oReservedVariables.Contains(sVariableCheck) Then sGames &= vbCrLf & oGame.Name & " (" & sVariableCheck & ")" bClean = False End If @@ -365,8 +527,12 @@ Public Class mgrPath Return bClean End Function - Public Shared Sub CustomVariablesReload() + Public Shared Sub LoadCustomVariables() hshCustomVariables = mgrVariables.ReadVariables + + For Each oVariable As clsPathVariable In hshCustomVariables.Values + Environment.SetEnvironmentVariable(oVariable.Name, oVariable.Path) + Next End Sub Public Shared Function SetManualGamePath() As String diff --git a/GBM/Managers/mgrProcessDetection.vb b/GBM/Managers/mgrProcessDetection.vb index cfc4216..3d4f3e7 100644 --- a/GBM/Managers/mgrProcessDetection.vb +++ b/GBM/Managers/mgrProcessDetection.vb @@ -5,13 +5,13 @@ Imports System.Text.RegularExpressions Public Class mgrProcessDetection Private prsFoundProcess As Process + Private sProcessPath As String Private dStartTime As DateTime = Now, dEndTime As DateTime = Now Private lTimeSpent As Long = 0 Private oGame As clsGame Private oDuplicateGames As New ArrayList Private bDuplicates As Boolean Private bVerified As Boolean = False - Private sFullCommand As String = String.Empty Property FoundProcess As Process Get @@ -22,6 +22,15 @@ Public Class mgrProcessDetection End Set End Property + Property ProcessPath As String + Get + Return sProcessPath + End Get + Set(value As String) + sProcessPath = value + End Set + End Property + Property StartTime As DateTime Get Return dStartTime @@ -73,85 +82,32 @@ Public Class mgrProcessDetection End Set End Property - Property FullCommand As String - Get - Return sFullCommand - End Get - Set(value As String) - sFullCommand = value - End Set - End Property - - Private Function HandleDuplicates(hshScanList As Hashtable) As Boolean - Dim sProcess As String - Dim sParameter As String = String.Empty - Dim bParameter As Boolean = False - Dim oInitialDupes As New ArrayList - - bDuplicates = True - oDuplicateGames.Clear() - - - For Each o As clsGame In hshScanList.Values - sProcess = o.ProcessName.Split(":")(0) - If o.Duplicate = True And (sProcess = oGame.TrueProcess Or Regex.IsMatch(sProcess, oGame.TrueProcess)) Then - oInitialDupes.Add(o.ShallowCopy) - End If - Next - - For Each o As clsGame In oInitialDupes - If (o.Parameter <> String.Empty And FullCommand.Contains(o.Parameter)) Then - sParameter = o.Parameter - bParameter = True - Exit For - End If - Next - - If bParameter Then - For Each o As clsGame In oInitialDupes - If (o.Parameter = sParameter) Then - oDuplicateGames.Add(o.ShallowCopy) - End If - Next - Else - For Each o As clsGame In oInitialDupes - If (o.Parameter = String.Empty) Then - oDuplicateGames.Add(o.ShallowCopy) - End If - Next - End If - - If oDuplicateGames.Count = 1 Then - oGame = DirectCast(oDuplicateGames(0), clsGame).ShallowCopy - Return True - End If - - Return False - End Function - 'This function will only work correctly on Windows - Private Sub GetWindowsCommand(ByVal prs As Process) - FullCommand = String.Empty + Private Function GetWindowsCommand(ByVal prs As Process) As String + Dim sFullCommand As String = String.Empty Try Using searcher As New ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + prs.Id.ToString) For Each o As ManagementObject In searcher.Get() - FullCommand &= o("CommandLine") & " " + sFullCommand &= o("CommandLine") & " " Next End Using - Catch ex As Exception + Catch 'Do Nothing End Try - End Sub + Return sFullCommand + End Function 'This function will only work correctly on Unix - Private Sub GetUnixCommand(ByVal prs As Process) - FullCommand = String.Empty + Private Function GetUnixCommand(ByVal prs As Process) As String + Dim sFullCommand As String = String.Empty Try - FullCommand = File.ReadAllText("/proc/" & prs.Id.ToString() & "/cmdline").Replace(vbNullChar, " ") - Catch ex As Exception + sFullCommand = File.ReadAllText("/proc/" & prs.Id.ToString() & "/cmdline").Replace(vbNullChar, " ") + Catch 'Do Nothing End Try - End Sub + + Return sFullCommand + End Function 'This function will only work correctly on Unix Private Function GetUnixProcessArguments(ByVal prs As Process) As String() @@ -202,15 +158,104 @@ Public Class mgrProcessDetection Return False End Function - Public Function SearchRunningProcesses(ByVal hshScanList As Hashtable, ByRef bNeedsPath As Boolean, ByRef iErrorCode As Integer, ByVal bDebugMode As Boolean) As Boolean + Private Function GetProcessPath(ByVal bWineProcess As Boolean) As String + Try + If Not bWineProcess Then + Return Path.GetDirectoryName(FoundProcess.MainModule.FileName) + Else + Return GetUnixSymLinkDirectory(FoundProcess) + End If + Catch + Return String.Empty + End Try + End Function + + Private Sub FilterDetected(ByVal oDetectedGames As ArrayList, ByVal bWineProcess As Boolean) + Dim bMatch As Boolean = False + Dim sFullCommand As String + Dim oNotDetectedWithParameters As New ArrayList + Dim oDetectedWithParameters As New ArrayList + Dim oNotDetectedWithProcessPath As New ArrayList + Dim oDetectedWithProcessPath As New ArrayList + + 'Get parameters of the found process + If mgrCommon.IsUnix Then + sFullCommand = GetUnixCommand(FoundProcess) + Else + sFullCommand = GetWindowsCommand(FoundProcess) + End If + + 'Get Process Path + ProcessPath = GetProcessPath(bWineProcess) + + 'Look for any games using parameters and any matches + For Each oDetectedGame As clsGame In oDetectedGames + If oDetectedGame.Parameter <> String.Empty Then + If sFullCommand.Contains(oDetectedGame.Parameter) Then + oDetectedWithParameters.Add(oDetectedGame) + Else + oNotDetectedWithParameters.Add(oDetectedGame) + End If + + End If + Next + + 'If we detected at least one parameter match, replace full detected list with the detected with parameter list + If oDetectedWithParameters.Count > 0 Then + oDetectedGames = oDetectedWithParameters + Else + 'If there is no parameter match, remove any games using parameters from the detected list + For Each oGameNotDetected As clsGame In oNotDetectedWithParameters + oDetectedGames.Remove(oGameNotDetected) + Next + End If + + 'If there's only one match after parameter detection, set it as current game and we're done. + If oDetectedGames.Count = 1 Then + GameInfo = oDetectedGames(0) + Duplicate = False + Else + 'Check if we have any exact matches based on process path + For Each oDetectedGame As clsGame In oDetectedGames + If oDetectedGame.ProcessPath <> String.Empty Then + If oDetectedGame.ProcessPath = ProcessPath Then + oDetectedWithProcessPath.Add(oDetectedGame) + Else + oNotDetectedWithProcessPath.Add(oDetectedGame) + End If + End If + Next + + 'If there's only one match after process detection, set it as current game and we're done + If oDetectedWithProcessPath.Count = 1 Then + GameInfo = oDetectedWithProcessPath(0) + Duplicate = False + Else + 'Remove any games with a process path that does not match the current process + For Each oGameNotDetected As clsGame In oNotDetectedWithProcessPath + oDetectedGames.Remove(oGameNotDetected) + Next + + 'If only a single game remains, set it as current game and we're done + If oDetectedGames.Count = 1 Then + GameInfo = oDetectedGames(0) + Duplicate = False + Else + 'We've done all we can, the user must selected which game they were playing when the process ends + Duplicate = True + oDuplicateGames = oDetectedGames + End If + End If + End If + End Sub + + Public Function SearchRunningProcesses(ByVal hshScanList As Hashtable, ByRef bNeedsPath As Boolean, ByRef bWineProcess As Boolean, ByRef iErrorCode As Integer, ByVal bDebugMode As Boolean) As Boolean Dim prsList() As Process = Process.GetProcesses Dim sProcessCheck As String = String.Empty Dim sProcessList As String = String.Empty - Dim bWineProcess As Boolean = False - Dim bPass As Boolean + Dim oDetectedGames As New ArrayList For Each prsCurrent As Process In prsList - bPass = False 'This needs to be wrapped due to issues with Mono. Try @@ -219,10 +264,13 @@ Public Class mgrProcessDetection 'Unix Handler 'We need some special handling for Wine processes If mgrCommon.IsUnix And (sProcessCheck.ToLower = "wine-preloader" Or sProcessCheck.ToLower = "wine64-preloader") Then + Dim sArgs As String() = GetUnixProcessArguments(prsCurrent) + Dim sParameter As String Dim sWinePath As String() - 'We can't use Path.GetFileName here, Wine uses the Windows seperator in arguments and Mono expects a different one in Unix. - sWinePath = GetUnixProcessArguments(prsCurrent)(0).Split("\") - sProcessCheck = sWinePath(sWinePath.Length - 1).Replace(".exe", "") + 'The wine-preloader parameters can refer to a path on the host system, windows based path within in the prefix, or mixed notation. + sParameter = sArgs(0).Replace("\", "/") + sWinePath = sParameter.Split("/") + sProcessCheck = Path.GetFileNameWithoutExtension(sWinePath(sWinePath.Length - 1)) bWineProcess = True Else bWineProcess = False @@ -237,39 +285,20 @@ Public Class mgrProcessDetection 'Do Nothing End Try - 'Detection Pass 1 For Each oCurrentGame As clsGame In hshScanList.Values If IsMatch(oCurrentGame, sProcessCheck) Then prsFoundProcess = prsCurrent oGame = oCurrentGame.ShallowCopy - bPass = True - - If mgrCommon.IsUnix Then - GetUnixCommand(prsCurrent) - Else - GetWindowsCommand(prsCurrent) - End If - - If oGame.Duplicate = True Then - If HandleDuplicates(hshScanList) Then - bDuplicates = False - oDuplicateGames.Clear() - End If - Else - bDuplicates = False - oDuplicateGames.Clear() - End If - - If Duplicate And DuplicateList.Count = 0 Then bPass = False - - If oGame.Parameter <> String.Empty And Not Duplicate And Not FullCommand.Contains(oGame.Parameter) Then bPass = False + oDetectedGames.Add(oGame.ShallowCopy) End If Next - 'Detection Pass 2 - If bPass Then - 'Determine the process path if we need it - If Not oGame.AbsolutePath Or oGame.Duplicate Then + If oDetectedGames.Count > 0 Then + FilterDetected(oDetectedGames, bWineProcess) + End If + + If oDetectedGames.Count > 0 Then + If Not oGame.AbsolutePath And Not oGame.MonitorOnly Then Try If Not bWineProcess Then oGame.ProcessPath = Path.GetDirectoryName(prsCurrent.MainModule.FileName) @@ -277,9 +306,6 @@ Public Class mgrProcessDetection oGame.ProcessPath = GetUnixSymLinkDirectory(prsCurrent) End If Catch exWin32 As System.ComponentModel.Win32Exception - 'If an exception occurs the process is: - 'Running as administrator and the app isn't. - 'The process is 64-bit and the process folder is required, shouldn't happen often. If exWin32.NativeErrorCode = 5 Then bNeedsPath = True iErrorCode = 5 @@ -288,16 +314,16 @@ Public Class mgrProcessDetection iErrorCode = 299 Else If bDebugMode Then mgrCommon.ShowMessage(exWin32.NativeErrorCode & " " & exWin32.Message & vbCrLf & vbCrLf & exWin32.StackTrace, MsgBoxStyle.Critical) - 'A different failure occured, drop out and continue to scan. - bPass = False + Return False End If Catch exAll As Exception If bDebugMode Then mgrCommon.ShowMessage(exAll.Message & vbCrLf & vbCrLf & exAll.StackTrace, MsgBoxStyle.Critical) - 'A different failure occured, drop out and continue to scan. - bPass = False + Return False End Try End If + If bDebugMode Then mgrCommon.SaveText(sProcessList, mgrPath.SettingsRoot & "/gbm_process_list.txt") + 'This will force two cycles for detection to try and prevent issues with UAC prompt If Not bVerified Then bVerified = True @@ -309,8 +335,6 @@ Public Class mgrProcessDetection End If Next - If bDebugMode Then mgrCommon.SaveText(sProcessList, mgrPath.SettingsRoot & "/gbm_process_list.txt") - Return False End Function diff --git a/GBM/Managers/mgrRestore.vb b/GBM/Managers/mgrRestore.vb index 91dd280..3f89ac9 100644 --- a/GBM/Managers/mgrRestore.vb +++ b/GBM/Managers/mgrRestore.vb @@ -37,7 +37,7 @@ Public Class mgrRestore If oGame.ProcessPath <> String.Empty Then oRestoreInfo.RelativeRestorePath = oGame.ProcessPath & Path.DirectorySeparatorChar & oRestoreInfo.RestorePath Else - sProcess = oGame.TrueProcess + sProcess = oGame.ProcessName If mgrCommon.IsProcessNotSearchable(oGame) Then bNoAuto = True sRestorePath = mgrPath.ProcessPathSearch(oRestoreInfo.Name, sProcess, mgrCommon.FormatString(mgrRestore_RelativeNeedPath, oRestoreInfo.Name), bNoAuto) diff --git a/GBM/Managers/mgrSQLite.vb b/GBM/Managers/mgrSQLite.vb index 17d9b66..7ead303 100644 --- a/GBM/Managers/mgrSQLite.vb +++ b/GBM/Managers/mgrSQLite.vb @@ -75,7 +75,7 @@ Public Class mgrSQLite "BackupFolder TEXT NOT NULL, StartWithWindows BOOLEAN NOT NULL, TimeTracking BOOLEAN NOT NULL, " & "SuppressBackup BOOLEAN NOT NULL, SuppressBackupThreshold INTEGER NOT NULL, CompressionLevel INTEGER NOT NULL, Custom7zArguments TEXT, " & "Custom7zLocation TEXT, SyncFields INTEGER NOT NULL, AutoSaveLog BOOLEAN NOT NULL, AutoRestore BOOLEAN NOT NULL, AutoMark BOOLEAN NOT NULL, SessionTracking BOOLEAN NOT NULL, " & - "SuppressMessages INTEGER NOT NULL, BackupOnLaunch BOOLEAN NOT NULL, UseGameID BOOLEAN NOT NULL, DisableSyncMessages BOOLEAN NOT NULL);" + "SuppressMessages INTEGER NOT NULL, BackupOnLaunch BOOLEAN NOT NULL, UseGameID BOOLEAN NOT NULL, DisableSyncMessages BOOLEAN NOT NULL, ShowResolvedPaths BOOLEAN NOT NULL);" 'Add Tables (SavedPath) sSql &= "CREATE TABLE savedpath (PathName TEXT NOT NULL PRIMARY KEY, Path TEXT NOT NULL);" @@ -848,6 +848,50 @@ Public Class mgrSQLite CompactDatabase() End If End If + + '1.15 Upgrade + If GetDatabaseVersion() < 115 Then + If eDatabase = Database.Local Then + 'Backup DB before starting + BackupDB("v110") + + 'Add new setting + sSQL = "ALTER TABLE settings ADD COLUMN ShowResolvedPaths BOOLEAN NOT NULL DEFAULT 1;" + + sSQL &= "PRAGMA user_version=115" + + RunParamQuery(sSQL, New Hashtable) + End If + If eDatabase = Database.Remote Then + 'Backup DB before starting + BackupDB("v110") + + 'Convert core path variables to new standard + If Not mgrCommon.IsUnix Then + sSQL = "UPDATE monitorlist SET Path = Replace(Path,'*appdatalocal*','%LOCALAPPDATA%');" + sSQL &= "UPDATE monitorlist SET Path = Replace(Path,'*appdataroaming*','%APPDATA%');" + sSQL &= "UPDATE monitorlist SET Path = Replace(Path,'*mydocs*','%USERDOCUMENTS%');" + sSQL &= "UPDATE monitorlist SET Path = Replace(Path,'*currentuser*','%USERPROFILE%');" + sSQL &= "UPDATE monitorlist SET Path = Replace(Path,'*publicdocs*','%COMMONDOCUMENTS%');" + Else + sSQL = "UPDATE monitorlist SET Path = Replace(Path,'*appdatalocal*','${XDG_DATA_HOME:-~/.local/share}');" + sSQL &= "UPDATE monitorlist SET Path = Replace(Path,'*appdataroaming*','${XDG_CONFIG_HOME:-~/.config}');" + sSQL &= "UPDATE monitorlist SET Path = Replace(Path,'*mydocs*','~');" + End If + + 'Convert custom variables to new standard + Dim hshVariables As Hashtable = mgrVariables.ReadVariables() + Dim sOldVariable As String + For Each oVariable As clsPathVariable In hshVariables.Values + sOldVariable = "*" & oVariable.Name & "*" + sSQL &= "UPDATE monitorlist SET Path = Replace(Path,'" & sOldVariable & "','" & oVariable.FormattedName & "');" + Next + + sSQL &= "PRAGMA user_version=115" + + RunParamQuery(sSQL, New Hashtable) + End If + End If End Sub Public Function GetDBSize() As Long diff --git a/GBM/Managers/mgrSettings.vb b/GBM/Managers/mgrSettings.vb index d7706b1..a11ec1a 100644 --- a/GBM/Managers/mgrSettings.vb +++ b/GBM/Managers/mgrSettings.vb @@ -25,10 +25,13 @@ Public Class mgrSettings Private bBackupOnLaunch As Boolean = True Private bUseGameID As Boolean = False Private bDisableSyncMessages As Boolean = True + Private bShowResolvedPaths As Boolean = True Public Enum eSuppressMessages None = 0 GameIDSync = 1 + BackupImport = 2 + WinConfigsInLinux = 4 End Enum Property StartWithWindows As Boolean @@ -175,6 +178,15 @@ Public Class mgrSettings End Set End Property + Property ShowResolvedPaths As Boolean + Get + Return bShowResolvedPaths + End Get + Set(value As Boolean) + bShowResolvedPaths = value + End Set + End Property + ReadOnly Property Prepared7zArguments As String Get 'Prepare custom 7z arguments @@ -309,7 +321,7 @@ Public Class mgrSettings sSQL &= "@CreateSubFolder, @ShowOverwriteWarning, @RestoreOnLaunch, @BackupFolder, @StartWithWindows, " sSQL &= "@TimeTracking, @SuppressBackup, @SuppressBackupThreshold, @CompressionLevel, @Custom7zArguments, @Custom7zLocation, " sSQL &= "@SyncFields, @AutoSaveLog, @AutoRestore, @AutoMark, @SessionTracking, @SuppressMessages, @BackupOnLaunch, @UseGameID, " - sSQL &= "@DisableSyncMessages)" + sSQL &= "@DisableSyncMessages, @ShowResolvedPaths)" hshParams.Add("MonitorOnStartup", MonitorOnStartup) hshParams.Add("StartToTray", StartToTray) @@ -335,6 +347,7 @@ Public Class mgrSettings hshParams.Add("BackupOnLaunch", BackupOnLaunch) hshParams.Add("UseGameID", UseGameID) hshParams.Add("DisableSyncMessages", DisableSyncMessages) + hshParams.Add("ShowResolvedPaths", ShowResolvedPaths) oDatabase.RunParamQuery(sSQL, hshParams) End Sub @@ -374,6 +387,7 @@ Public Class mgrSettings BackupOnLaunch = CBool(dr("BackupOnLaunch")) UseGameID = CBool(dr("UseGameID")) DisableSyncMessages = CBool(dr("DisableSyncMessages")) + ShowResolvedPaths = CBool(dr("ShowResolvedPaths")) Next oDatabase.Disconnect() diff --git a/GBM/Managers/mgrVariables.vb b/GBM/Managers/mgrVariables.vb index 04613a9..2198cbd 100644 --- a/GBM/Managers/mgrVariables.vb +++ b/GBM/Managers/mgrVariables.vb @@ -154,4 +154,29 @@ Return hshList End Function + + Public Shared Function GetReservedVariables() As List(Of String) + Dim oList As New List(Of String) + + oList.Add("APPDATA") + oList.Add("LOCALAPPDATA") + oList.Add("USERDOCUMENTS") + oList.Add("COMMONDOCUMENTS") + oList.Add("USERPROFILE") + + Return oList + End Function + + Public Shared Function CheckForReservedVariables(ByVal sPath As String) As Boolean + Dim s As String + + For Each s In GetReservedVariables() + s = "%" & s & "%" + If sPath.Contains(s) Then + Return True + End If + Next + + Return False + End Function End Class diff --git a/GBM/My Project/Resources.Designer.vb b/GBM/My Project/Resources.Designer.vb index def094c..b409610 100644 --- a/GBM/My Project/Resources.Designer.vb +++ b/GBM/My Project/Resources.Designer.vb @@ -1410,6 +1410,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to 7-Zip. + ''' + Friend ReadOnly Property frmGameManager_7zBackup() As String + Get + Return ResourceManager.GetString("frmGameManager_7zBackup", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to [PARAM] ([PARAM]). ''' @@ -1465,7 +1474,7 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to &Cancel. + ''' Looks up a localized string similar to Ca&ncel. ''' Friend ReadOnly Property frmGameManager_btnCancel() As String Get @@ -1555,7 +1564,16 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to In&clude Items.... + ''' Looks up a localized string similar to Import B&ackup Files. + ''' + Friend ReadOnly Property frmGameManager_btnImportBackup() As String + Get + Return ResourceManager.GetString("frmGameManager_btnImportBackup", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Incl&ude Items.... ''' Friend ReadOnly Property frmGameManager_btnInclude() As String Get @@ -1599,6 +1617,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to Pro&cesses.... + ''' + Friend ReadOnly Property frmGameManager_btnProcesses() As String + Get + Return ResourceManager.GetString("frmGameManager_btnProcesses", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to &Restore. ''' @@ -1627,7 +1654,7 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to Tags.... + ''' Looks up a localized string similar to &Tags.... ''' Friend ReadOnly Property frmGameManager_btnTags() As String Get @@ -1689,6 +1716,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to Choose compatible backup file(s). + ''' + Friend ReadOnly Property frmGameManager_Choose7zImport() As String + Get + Return ResourceManager.GetString("frmGameManager_Choose7zImport", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Choose a custom icon for the game. ''' @@ -1779,6 +1815,24 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to &Linux.... + ''' + Friend ReadOnly Property frmGameManager_cmsOfficialLinux() As String + Get + Return ResourceManager.GetString("frmGameManager_cmsOfficialLinux", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to &Windows.... + ''' + Friend ReadOnly Property frmGameManager_cmsOfficialWindows() As String + Get + Return ResourceManager.GetString("frmGameManager_cmsOfficialWindows", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Are you sure you want to run a backup for [PARAM]? This will close the form.. ''' @@ -1824,6 +1878,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to Are you sure you want to import the selected file(s) for [PARAM]. This will close the form.. + ''' + Friend ReadOnly Property frmGameManager_ConfirmBackupImport() As String + Get + Return ResourceManager.GetString("frmGameManager_ConfirmBackupImport", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Are you sure you want to delete [PARAM]? This cannot be undone.[BR][BR]This will not delete any backup files that already exist for this game.. ''' @@ -2158,7 +2221,7 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to In&clude. + ''' Looks up a localized string similar to Incl&ude. ''' Friend ReadOnly Property frmGameManager_IncludeShortcut() As String Get @@ -2400,6 +2463,33 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to This feature is intended to import 7-Zip backup archives compatible with the selected game configuration.[BR][BR]GBM does NOT verify these files, you should only use files created by GBM or compatible archives created using the same settings as the current game configuration.[BR][BR]This message will only be shown once.. + ''' + Friend ReadOnly Property frmGameManager_WarningImportBackup() As String + Get + Return ResourceManager.GetString("frmGameManager_WarningImportBackup", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to The "Save multiple backups" option must be enabled on this configuration to import backup files.. + ''' + Friend ReadOnly Property frmGameManager_WarningImportBackupSaveMulti() As String + Get + Return ResourceManager.GetString("frmGameManager_WarningImportBackupSaveMulti", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Official Windows configurations can be used in Linux for games running in Wine or Proton.[BR][BR]You do not need to modify these configurations, GBM will automatically do any required path conversions the first time the game is detected.[BR][BR]This message will only be shown once.. + ''' + Friend ReadOnly Property frmGameManager_WarningWinConfigsInLinux() As String + Get + Return ResourceManager.GetString("frmGameManager_WarningWinConfigsInLinux", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to XML. ''' @@ -3876,6 +3966,24 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to [PARAM] Wine Prefix: [PARAM]. + ''' + Friend ReadOnly Property frmMain_WinePrefix() As String + Get + Return ResourceManager.GetString("frmMain_WinePrefix", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to [PARAM] Converted Save Path: [PARAM]. + ''' + Friend ReadOnly Property frmMain_WineSavePath() As String + Get + Return ResourceManager.GetString("frmMain_WineSavePath", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to +. ''' @@ -4542,6 +4650,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to Show resolved save paths. + ''' + Friend ReadOnly Property frmSettings_chkShowResolvedPaths() As String + Get + Return ResourceManager.GetString("frmSettings_chkShowResolvedPaths", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Start to system tray. ''' @@ -5361,6 +5478,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to [PARAM] is a reserved variable, you must enter a different name.. + ''' + Friend ReadOnly Property frmVariableManager_ErrorVariableReserved() As String + Get + Return ResourceManager.GetString("frmVariableManager_ErrorVariableReserved", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Custom Variable Manager. ''' @@ -5700,6 +5826,15 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to Failed to import [PARAM], the file could not be copied.. + ''' + Friend ReadOnly Property mgrBackup_ErrorImportBackupCopy() As String + Get + Return ResourceManager.GetString("mgrBackup_ErrorImportBackupCopy", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Backup aborted by user due to manifest conflict.. ''' @@ -5746,7 +5881,7 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to Backup aborted. A failure occured while creating a backup sub-folder for [PARAM].[BR][PARAM]. + ''' Looks up a localized string similar to A failure occured while creating a backup sub-folder for [PARAM].[BR][PARAM]. ''' Friend ReadOnly Property mgrBackup_ErrorSubFolderCreate() As String Get @@ -5763,6 +5898,24 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to Imported Backup. + ''' + Friend ReadOnly Property mgrBackup_ImportedFile() As String + Get + Return ResourceManager.GetString("mgrBackup_ImportedFile", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Imported backup file [PARAM] for [PARAM].. + ''' + Friend ReadOnly Property mgrBackup_ImportSuccess() As String + Get + Return ResourceManager.GetString("mgrBackup_ImportSuccess", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Available Disk Space: [PARAM]. ''' @@ -5980,7 +6133,7 @@ Namespace My.Resources End Property ''' - ''' Looks up a localized string similar to This export file was created with a version of GBM prior to 1.1.0 and does not contain unique game identifiers.[BR][BR]Do you still want to import configurations from this file? (Not Recommended). + ''' Looks up a localized string similar to This export file was created with a version of GBM prior to 1.1.5 and may contain incompatible configuration data.[BR][BR]Do you still want to import configurations from this file? (Not Recommended). ''' Friend ReadOnly Property mgrMonitorList_ImportVersionWarning() As String Get @@ -6087,6 +6240,42 @@ Namespace My.Resources End Get End Property + ''' + ''' Looks up a localized string similar to An error occured when building a Wine path.[BR][BR][PARAM]. + ''' + Friend ReadOnly Property mgrPath_ErrorBuildingWinePath() As String + Get + Return ResourceManager.GetString("mgrPath_ErrorBuildingWinePath", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to An error occured when converting to a Wine save path.[BR][BR][PARAM]. + ''' + Friend ReadOnly Property mgrPath_ErrorConvertWineSavePath() As String + Get + Return ResourceManager.GetString("mgrPath_ErrorConvertWineSavePath", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to An error occured while building a list of environment variables.[BR][BR][PARAM]. + ''' + Friend ReadOnly Property mgrPath_ErrorUnixEnv() As String + Get + Return ResourceManager.GetString("mgrPath_ErrorUnixEnv", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to An error occured when determining a Wine prefix.[BR][BR][PARAM]. + ''' + Friend ReadOnly Property mgrPath_ErrorWinePrefix() As String + Get + Return ResourceManager.GetString("mgrPath_ErrorWinePrefix", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to An error occured while determining a required system path. [BR][BR]The environment value for [PARAM] is empty.[BR][BR]The application will now exit.. ''' diff --git a/GBM/My Project/Resources.resx b/GBM/My Project/Resources.resx index 8f16cb6..7ab3bcf 100644 --- a/GBM/My Project/Resources.resx +++ b/GBM/My Project/Resources.resx @@ -431,7 +431,7 @@ &Backup - &Cancel + Ca&ncel C&lose @@ -455,7 +455,7 @@ &Import - In&clude Items... + Incl&ude Items... &Mark as Restored @@ -479,7 +479,7 @@ ... - Tags... + &Tags... Monitor this game @@ -590,7 +590,7 @@ Include - In&clude + Incl&ude Items... @@ -1307,7 +1307,7 @@ [PARAM] backup aborted by user due to overwrite. - Backup aborted. A failure occured while creating a backup sub-folder for [PARAM].[BR][PARAM] + A failure occured while creating a backup sub-folder for [PARAM].[BR][PARAM] Generating SHA-256 hash for [PARAM] backup file. @@ -2027,7 +2027,7 @@ This file cannot be used to sync game indentifiers. It was created with an older version of GBM. - This export file was created with a version of GBM prior to 1.1.0 and does not contain unique game identifiers.[BR][BR]Do you still want to import configurations from this file? (Not Recommended) + This export file was created with a version of GBM prior to 1.1.5 and may contain incompatible configuration data.[BR][BR]Do you still want to import configurations from this file? (Not Recommended) Do you want to sync your game identifiers with this export file?[BR][BR]You should only do this if you're managing your own game configurations. If you use GBM on multiple PCs, please see "Tools" section of the online manual before using this feature. @@ -2203,4 +2203,67 @@ The backup folder [PARAM] cannot be renamed to [PARAM] because a folder with that name already exists.[BR][BR]Do you want to continue and remove the existing folder? + + [PARAM] Wine Prefix: [PARAM] + + + [PARAM] Converted Save Path: [PARAM] + + + An error occured when building a Wine path.[BR][BR][PARAM] + + + An error occured when converting to a Wine save path.[BR][BR][PARAM] + + + An error occured when determining a Wine prefix.[BR][BR][PARAM] + + + An error occured while building a list of environment variables.[BR][BR][PARAM] + + + Show resolved save paths + + + 7-Zip + + + Import B&ackup Files + + + Choose compatible backup file(s) + + + Are you sure you want to import the selected file(s) for [PARAM]. This will close the form. + + + Failed to import [PARAM], the file could not be copied. + + + Imported Backup + + + Imported backup file [PARAM] for [PARAM]. + + + Pro&cesses... + + + This feature is intended to import 7-Zip backup archives compatible with the selected game configuration.[BR][BR]GBM does NOT verify these files, you should only use files created by GBM or compatible archives created using the same settings as the current game configuration.[BR][BR]This message will only be shown once. + + + [PARAM] is a reserved variable, you must enter a different name. + + + The "Save multiple backups" option must be enabled on this configuration to import backup files. + + + &Linux... + + + &Windows... + + + Official Windows configurations can be used in Linux for games running in Wine or Proton.[BR][BR]You do not need to modify these configurations, GBM will automatically do any required path conversions the first time the game is detected.[BR][BR]This message will only be shown once. + \ No newline at end of file diff --git a/GBM/readme.txt b/GBM/readme.txt index e5b5f9b..a618210 100644 --- a/GBM/readme.txt +++ b/GBM/readme.txt @@ -1,25 +1,58 @@ -Game Backup Monitor v1.1.4 Readme +Game Backup Monitor v1.1.5 Readme http://mikemaximus.github.io/gbm-web/ gamebackupmonitor@gmail.com -August 25th, 2018 +October 1st, 2018 -New in 1.1.4 +New in 1.1.5 All Platforms: -- Changed method of handling required OS environment paths and added a new path check on startup. -- Incorrect conflict warnings will no longer be shown in some situations when running a backup configuration with "Save Multiple Backups" enabled. -- Fixed the precision of "Total Hours" on the Session Viewer, it now always rounds to 2 decimal places. - -Windows: - -- GBM will no longer keep an open file handle on detected processes and custom icons. - +- Fixed a crash that could occur when renaming a game configuration. +- Rewrote portions of game detection: + - The handling of duplicate game configurations has been flawed since regular expression support was added in v1.1. This has been fixed. + - GBM is now more efficient when detecting games by parameter and/or process path. +- Changed how save path variables are handled: + - GBM no longer uses it's own format and naming scheme for path variables. + - GBM now uses Windows environment variables in game configurations. + - For example, *appdatalocal* is now %LOCALAPPDATA% in a Windows configuration. + - GBM also uses some custom environment variables, such as %USERDOCUMENTS% and %COMMONDOCUMENTS%. These are needed for compatability and to handle some limitations with how GBM interacts with 7-Zip. + - GBM now uses the XDG specification for game configurations in Linux. (Thanks basxto!) + - For example, *appdatalocal* is now ${XDG_DATA_HOME:-~/.local/share} in a Linux configuration. + - Custom Path Variables have changed. For example, *Steam User Data* will now be %Steam User Data%. + - They will appear this way in Windows and Linux. + - GBM will no longer allow the creation of variables using reserved names, such as APPDATA. + - Your configurations will be automatically updated to these new formats when upgrading to v1.1.5. + - These changes will break game list compatability with other versions of GBM. Archived lists are available at http://mikemaximus.github.io/gbm-web/archive.html for those that wish to stay on an older version. +- Added a new setting, "Show resolved save paths". + - This new setting is enabled by default. + - When enabled, GBM will display resolved save paths on the Game Manager. This is how GBM displayed paths prior to v1.1.5. + - When disabled, GBM will display save paths with their variables when applicable. +- Added a tooltip to applicable "Path" fields on the Game Manager. + - This tooltip either displays either a resolved or unresolved path. + - The behaviour is toggled by the "Show resolved save paths" setting. +-Added "Import Backup Files" feature to the Game Manager. + - This feature allows you to import one or more backup files for a specific game configuration. + - This is useful if you lost your GBM database(s), but not the backup files. It also can be used to easily move compatible saved game backups between Windows and Linux. + - GBM cannot verify that the backups being imported are compatible with the current configuration. This is up to the user! + - This feature will be expanded and refined in future releases. + Linux: -- Sorting by "Hours" in the Session Viewer will no longer crash GBM on certain systems. -- GBM no longer attempts to get icons from a Linux binary. -- Removed compression from GBM icon to prevent issues with recent versions of imagemagick. +- Fixed an issue that prevented Wine / Proton games from being detected in some cases. +- GBM can now use any Windows configuration to detect and backup games running in Wine / Proton. + - An absolute Windows save path, such "%APPDATA%\Game\Saved Games" will be automatically converted to the proper path within the detected Wine prefix. + - The converted path will be saved to the configuration once game has been detected at least once. + - You cannot restore a backup using an absolute Windows path. The game needs to be detected at least once so the correct save path can be determined. + - This feature should be considered "Beta" and may not work in all scenarios. Please report any issues you may encounter! + +Website: + +- Search features have been added to the Official Game Lists (Thanks basxto!) + - Includes advanced features such as searching by tag and excludes. + - Tags are now clickable for automatic searching of similar games. + - Searches are linkable. + - You can now link directly to a single configuration using the new link icon. + - Configurations intelligently collapse or uncollapse based on search results. The entire version history of GBM releases is available at http://mikemaximus.github.io/gbm-web/versionhistory.html \ No newline at end of file