diff --git a/.vs/OpenSaveCloudClient/DesignTimeBuild/.dtbcache.v2 b/.vs/OpenSaveCloudClient/DesignTimeBuild/.dtbcache.v2 index f76d425..32460a3 100644 Binary files a/.vs/OpenSaveCloudClient/DesignTimeBuild/.dtbcache.v2 and b/.vs/OpenSaveCloudClient/DesignTimeBuild/.dtbcache.v2 differ diff --git a/.vs/OpenSaveCloudClient/FileContentIndex/00d971d2-9af0-4795-a67e-9693f9bcc8db.vsidx b/.vs/OpenSaveCloudClient/FileContentIndex/00d971d2-9af0-4795-a67e-9693f9bcc8db.vsidx new file mode 100644 index 0000000..388068f Binary files /dev/null and b/.vs/OpenSaveCloudClient/FileContentIndex/00d971d2-9af0-4795-a67e-9693f9bcc8db.vsidx differ diff --git a/.vs/OpenSaveCloudClient/FileContentIndex/4829ade5-91f1-4fac-a250-dde6c2fbc3e0.vsidx b/.vs/OpenSaveCloudClient/FileContentIndex/4829ade5-91f1-4fac-a250-dde6c2fbc3e0.vsidx deleted file mode 100644 index cad1da8..0000000 Binary files a/.vs/OpenSaveCloudClient/FileContentIndex/4829ade5-91f1-4fac-a250-dde6c2fbc3e0.vsidx and /dev/null differ diff --git a/.vs/OpenSaveCloudClient/FileContentIndex/81f94a29-3946-4ebb-90ea-c2755ce6b7b5.vsidx b/.vs/OpenSaveCloudClient/FileContentIndex/81f94a29-3946-4ebb-90ea-c2755ce6b7b5.vsidx deleted file mode 100644 index 7029f0d..0000000 Binary files a/.vs/OpenSaveCloudClient/FileContentIndex/81f94a29-3946-4ebb-90ea-c2755ce6b7b5.vsidx and /dev/null differ diff --git a/.vs/OpenSaveCloudClient/FileContentIndex/b07c75de-4986-4fe4-8f3e-b5c6e7d95282.vsidx b/.vs/OpenSaveCloudClient/FileContentIndex/b07c75de-4986-4fe4-8f3e-b5c6e7d95282.vsidx new file mode 100644 index 0000000..5909d1c Binary files /dev/null and b/.vs/OpenSaveCloudClient/FileContentIndex/b07c75de-4986-4fe4-8f3e-b5c6e7d95282.vsidx differ diff --git a/.vs/OpenSaveCloudClient/v17/.futdcache.v1 b/.vs/OpenSaveCloudClient/v17/.futdcache.v1 index 6996ff2..d961798 100644 Binary files a/.vs/OpenSaveCloudClient/v17/.futdcache.v1 and b/.vs/OpenSaveCloudClient/v17/.futdcache.v1 differ diff --git a/.vs/ProjectEvaluation/opensavecloudclient.metadata.v2 b/.vs/ProjectEvaluation/opensavecloudclient.metadata.v2 index ede6e77..38f4856 100644 Binary files a/.vs/ProjectEvaluation/opensavecloudclient.metadata.v2 and b/.vs/ProjectEvaluation/opensavecloudclient.metadata.v2 differ diff --git a/.vs/ProjectEvaluation/opensavecloudclient.projects.v2 b/.vs/ProjectEvaluation/opensavecloudclient.projects.v2 index 2c8d023..3e971ad 100644 Binary files a/.vs/ProjectEvaluation/opensavecloudclient.projects.v2 and b/.vs/ProjectEvaluation/opensavecloudclient.projects.v2 differ diff --git a/OpenSaveCloudClient/Core/HashTool.cs b/OpenSaveCloudClient/Core/HashTool.cs index 0de8cef..c47ede3 100644 --- a/OpenSaveCloudClient/Core/HashTool.cs +++ b/OpenSaveCloudClient/Core/HashTool.cs @@ -79,5 +79,16 @@ namespace OpenSaveCloudClient.Core return sha.ComputeHash(inputStream); } + public static string? HashFile(string path) + { + FileInfo fileInfo = new(path); + byte[]? hash = HashFile(fileInfo); + if (hash == null) + { + throw new InvalidOperationException("HashFile: file to get hash"); + } + return BitConverter.ToString(hash).Replace("-", ""); + } + } } diff --git a/OpenSaveCloudClient/Core/ServerConnector.cs b/OpenSaveCloudClient/Core/ServerConnector.cs index 0ca9062..dbc6b8b 100644 --- a/OpenSaveCloudClient/Core/ServerConnector.cs +++ b/OpenSaveCloudClient/Core/ServerConnector.cs @@ -424,6 +424,13 @@ namespace OpenSaveCloudClient.Core FileStream stream = File.OpenRead(filePath); try { + string? hash = HashTool.HashFile(filePath); + if (hash == null) + { + logManager.AddError("Failed to get hash of archive"); + taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed); + return false; + } MultipartFormDataContent multipartFormContent = new(); var fileStreamContent = new StreamContent(stream); fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); @@ -433,6 +440,7 @@ namespace OpenSaveCloudClient.Core client.DefaultRequestHeaders.Add("Authorization", "bearer " + token); client.DefaultRequestHeaders.Add("X-Upload-Key", uploadToken); client.DefaultRequestHeaders.Add("X-Game-Save-Hash", newHash); + client.DefaultRequestHeaders.Add("X-Hash", hash); HttpResponseMessage response = client.PostAsync(string.Format("{0}:{1}/api/v1/game/upload", host, port), multipartFormContent).Result; if (response.IsSuccessStatusCode) { @@ -479,13 +487,34 @@ namespace OpenSaveCloudClient.Core { await response.Content.CopyToAsync(fs); } - if (Directory.Exists(unzipPath)) + + if (response.Headers.TryGetValues("X-Hash", out IEnumerable? hashValue)) { - Directory.Delete(unzipPath, true); + if (hashValue != null) + { + string hash = hashValue.First(); + string? localHash = HashTool.HashFile(filePath); + if (localHash != null) + { + if (hash != localHash) + { + logManager.AddError("The hash does not match"); + taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed); + return false; + } + if (Directory.Exists(unzipPath)) + { + Directory.Delete(unzipPath, true); + } + ZipFile.ExtractToDirectory(filePath, unzipPath); + taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended); + return true; + } + } } - ZipFile.ExtractToDirectory(filePath, unzipPath); - taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended); - return true; + logManager.AddError("Server does not send the hash of the archive"); + taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed); + return false; } else { diff --git a/OpenSaveCloudClient/Core/TaskManager.cs b/OpenSaveCloudClient/Core/TaskManager.cs index f2d217c..7da9281 100644 --- a/OpenSaveCloudClient/Core/TaskManager.cs +++ b/OpenSaveCloudClient/Core/TaskManager.cs @@ -11,6 +11,7 @@ namespace OpenSaveCloudClient.Core { private static TaskManager instance; + private static LogManager logManager; private readonly System.Timers.Timer timer; private readonly Dictionary _tasks; @@ -20,6 +21,7 @@ namespace OpenSaveCloudClient.Core private TaskManager() { + logManager = LogManager.GetInstance(); _tasks = new Dictionary(); mut = new Mutex(); timer = new System.Timers.Timer @@ -41,19 +43,34 @@ namespace OpenSaveCloudClient.Core public string StartTask(string label, bool undefined, int progressMax) { - string uuid = Guid.NewGuid().ToString(); - AsyncTaskInformation ati = new(uuid, label, undefined, progressMax); - _tasks.Add(uuid, ati); - TaskChangedEventArgs args = new() + mut.WaitOne(); + try { - TaskInformation = ati - }; - OnTaskChanged(args); - return uuid; + string uuid = Guid.NewGuid().ToString(); + AsyncTaskInformation ati = new(uuid, label, undefined, progressMax); + _tasks.Add(uuid, ati); + TaskChangedEventArgs args = new() + { + TaskInformation = ati + }; + OnTaskChanged(args); + return uuid; + } + catch (Exception ex) + { + logManager.AddError(ex); + throw; + } + finally + { + mut.ReleaseMutex(); + } + } public void UpdateTaskProgress(string uuid, int progress) { + mut.WaitOne(); try { AsyncTaskInformation task = _tasks[uuid]; @@ -65,14 +82,19 @@ namespace OpenSaveCloudClient.Core }; OnTaskChanged(args); } - catch(Exception) + catch(Exception ex) { - + logManager.AddError(ex); + } + finally + { + mut.ReleaseMutex(); } } public void UpdateTaskStatus(string uuid, AsyncTaskStatus status) { + mut.WaitOne(); try { AsyncTaskInformation task = _tasks[uuid]; @@ -88,9 +110,13 @@ namespace OpenSaveCloudClient.Core }; OnTaskChanged(args); } - catch (Exception) + catch (Exception ex) { - + logManager.AddError(ex); + } + finally + { + mut.ReleaseMutex(); } } @@ -99,22 +125,8 @@ namespace OpenSaveCloudClient.Core mut.WaitOne(); try { - List toDelete = new(); - foreach (KeyValuePair task in _tasks) - { - if (task.Value.Status == AsyncTaskStatus.Ended) - { - toDelete.Add(task.Key); - } - } - foreach (string uuid in toDelete) - { - _tasks.Remove(uuid); - } - if (toDelete.Count > 0) - { - OnTaskCleared(new TaskClearedEventArgs()); - } + _tasks.Clear(); + OnTaskCleared(new TaskClearedEventArgs()); } finally { diff --git a/OpenSaveCloudClient/GameLibraryForm.Designer.cs b/OpenSaveCloudClient/GameLibraryForm.Designer.cs index 13d3be4..d1584d8 100644 --- a/OpenSaveCloudClient/GameLibraryForm.Designer.cs +++ b/OpenSaveCloudClient/GameLibraryForm.Designer.cs @@ -56,13 +56,12 @@ this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; this.listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable; this.listView1.LargeImageList = this.coverList; - this.listView1.Location = new System.Drawing.Point(0, 80); - this.listView1.Margin = new System.Windows.Forms.Padding(2); + this.listView1.Location = new System.Drawing.Point(0, 120); this.listView1.MultiSelect = false; this.listView1.Name = "listView1"; this.listView1.ShowGroups = false; this.listView1.ShowItemToolTips = true; - this.listView1.Size = new System.Drawing.Size(1004, 406); + this.listView1.Size = new System.Drawing.Size(1506, 610); this.listView1.TabIndex = 2; this.listView1.UseCompatibleStateImageBehavior = false; this.listView1.DoubleClick += new System.EventHandler(this.listView1_DoubleClick); @@ -81,9 +80,10 @@ this.TasksButton, this.StatusLabel, this.MainProgressBar}); - this.statusStrip1.Location = new System.Drawing.Point(0, 486); + this.statusStrip1.Location = new System.Drawing.Point(0, 730); this.statusStrip1.Name = "statusStrip1"; - this.statusStrip1.Size = new System.Drawing.Size(1004, 25); + this.statusStrip1.Padding = new System.Windows.Forms.Padding(2, 0, 21, 0); + this.statusStrip1.Size = new System.Drawing.Size(1506, 36); this.statusStrip1.TabIndex = 4; this.statusStrip1.Text = "statusStrip1"; // @@ -94,7 +94,7 @@ this.TasksButton.ImageTransparentColor = System.Drawing.Color.Magenta; this.TasksButton.Name = "TasksButton"; this.TasksButton.ShowDropDownArrow = false; - this.TasksButton.Size = new System.Drawing.Size(32, 23); + this.TasksButton.Size = new System.Drawing.Size(46, 33); this.TasksButton.Text = ""; this.TasksButton.Click += new System.EventHandler(this.toolStripDropDownButton1_Click); // @@ -102,13 +102,13 @@ // this.StatusLabel.BackColor = System.Drawing.Color.Transparent; this.StatusLabel.Name = "StatusLabel"; - this.StatusLabel.Size = new System.Drawing.Size(0, 20); + this.StatusLabel.Size = new System.Drawing.Size(0, 29); // // MainProgressBar // this.MainProgressBar.MarqueeAnimationSpeed = 20; this.MainProgressBar.Name = "MainProgressBar"; - this.MainProgressBar.Size = new System.Drawing.Size(100, 19); + this.MainProgressBar.Size = new System.Drawing.Size(150, 28); this.MainProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; this.MainProgressBar.Visible = false; // @@ -126,10 +126,11 @@ this.panel1.Controls.Add(this.AddButton); this.panel1.Dock = System.Windows.Forms.DockStyle.Top; this.panel1.Location = new System.Drawing.Point(0, 0); - this.panel1.MaximumSize = new System.Drawing.Size(0, 80); - this.panel1.MinimumSize = new System.Drawing.Size(0, 80); + this.panel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); + this.panel1.MaximumSize = new System.Drawing.Size(0, 120); + this.panel1.MinimumSize = new System.Drawing.Size(0, 120); this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(1004, 80); + this.panel1.Size = new System.Drawing.Size(1506, 120); this.panel1.TabIndex = 5; // // AboutButton @@ -138,9 +139,10 @@ this.AboutButton.FlatAppearance.BorderSize = 0; this.AboutButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.AboutButton.Font = new System.Drawing.Font("Segoe MDL2 Assets", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.AboutButton.Location = new System.Drawing.Point(505, 3); + this.AboutButton.Location = new System.Drawing.Point(758, 4); + this.AboutButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.AboutButton.Name = "AboutButton"; - this.AboutButton.Size = new System.Drawing.Size(57, 74); + this.AboutButton.Size = new System.Drawing.Size(86, 111); this.AboutButton.TabIndex = 9; this.AboutButton.Text = "\r\n\r\nAbout"; this.AboutButton.UseVisualStyleBackColor = true; @@ -151,9 +153,10 @@ this.LogButton.FlatAppearance.BorderSize = 0; this.LogButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.LogButton.Font = new System.Drawing.Font("Segoe MDL2 Assets", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.LogButton.Location = new System.Drawing.Point(442, 3); + this.LogButton.Location = new System.Drawing.Point(663, 4); + this.LogButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.LogButton.Name = "LogButton"; - this.LogButton.Size = new System.Drawing.Size(57, 74); + this.LogButton.Size = new System.Drawing.Size(86, 111); this.LogButton.TabIndex = 8; this.LogButton.Text = "\r\n\r\nLog"; this.LogButton.UseVisualStyleBackColor = true; @@ -165,9 +168,10 @@ this.LogoutButton.FlatAppearance.BorderSize = 0; this.LogoutButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.LogoutButton.Font = new System.Drawing.Font("Segoe MDL2 Assets", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.LogoutButton.Location = new System.Drawing.Point(379, 3); + this.LogoutButton.Location = new System.Drawing.Point(568, 4); + this.LogoutButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.LogoutButton.Name = "LogoutButton"; - this.LogoutButton.Size = new System.Drawing.Size(57, 74); + this.LogoutButton.Size = new System.Drawing.Size(86, 111); this.LogoutButton.TabIndex = 7; this.LogoutButton.Text = "\r\n\r\nLogout"; this.LogoutButton.UseVisualStyleBackColor = true; @@ -178,9 +182,10 @@ this.label2.AutoSize = true; this.label2.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.label2.ForeColor = System.Drawing.Color.LightGray; - this.label2.Location = new System.Drawing.Point(353, 22); + this.label2.Location = new System.Drawing.Point(530, 33); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(20, 32); + this.label2.Size = new System.Drawing.Size(29, 48); this.label2.TabIndex = 6; this.label2.Text = "|"; // @@ -190,9 +195,10 @@ this.UserSettingsButton.FlatAppearance.BorderSize = 0; this.UserSettingsButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.UserSettingsButton.Font = new System.Drawing.Font("Segoe MDL2 Assets", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.UserSettingsButton.Location = new System.Drawing.Point(290, 3); + this.UserSettingsButton.Location = new System.Drawing.Point(435, 4); + this.UserSettingsButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.UserSettingsButton.Name = "UserSettingsButton"; - this.UserSettingsButton.Size = new System.Drawing.Size(57, 74); + this.UserSettingsButton.Size = new System.Drawing.Size(86, 111); this.UserSettingsButton.TabIndex = 5; this.UserSettingsButton.Text = "\r\n\r\nMe"; this.UserSettingsButton.UseVisualStyleBackColor = true; @@ -202,9 +208,10 @@ this.SettingsButton.FlatAppearance.BorderSize = 0; this.SettingsButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.SettingsButton.Font = new System.Drawing.Font("Segoe MDL2 Assets", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.SettingsButton.Location = new System.Drawing.Point(227, 3); + this.SettingsButton.Location = new System.Drawing.Point(340, 4); + this.SettingsButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.SettingsButton.Name = "SettingsButton"; - this.SettingsButton.Size = new System.Drawing.Size(57, 74); + this.SettingsButton.Size = new System.Drawing.Size(86, 111); this.SettingsButton.TabIndex = 4; this.SettingsButton.Text = "\r\n\r\nOptions"; this.SettingsButton.UseVisualStyleBackColor = true; @@ -215,9 +222,10 @@ this.label1.AutoSize = true; this.label1.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); this.label1.ForeColor = System.Drawing.Color.LightGray; - this.label1.Location = new System.Drawing.Point(201, 22); + this.label1.Location = new System.Drawing.Point(302, 33); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(20, 32); + this.label1.Size = new System.Drawing.Size(29, 48); this.label1.TabIndex = 3; this.label1.Text = "|"; // @@ -227,9 +235,10 @@ this.DownloadButton.FlatAppearance.BorderSize = 0; this.DownloadButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.DownloadButton.Font = new System.Drawing.Font("Segoe MDL2 Assets", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.DownloadButton.Location = new System.Drawing.Point(138, 3); + this.DownloadButton.Location = new System.Drawing.Point(207, 4); + this.DownloadButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.DownloadButton.Name = "DownloadButton"; - this.DownloadButton.Size = new System.Drawing.Size(57, 74); + this.DownloadButton.Size = new System.Drawing.Size(86, 111); this.DownloadButton.TabIndex = 2; this.DownloadButton.Text = "\r\n\r\nGet"; this.DownloadButton.UseVisualStyleBackColor = true; @@ -241,9 +250,10 @@ this.SyncButton.FlatAppearance.BorderSize = 0; this.SyncButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.SyncButton.Font = new System.Drawing.Font("Segoe MDL2 Assets", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.SyncButton.Location = new System.Drawing.Point(75, 3); + this.SyncButton.Location = new System.Drawing.Point(112, 4); + this.SyncButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.SyncButton.Name = "SyncButton"; - this.SyncButton.Size = new System.Drawing.Size(57, 74); + this.SyncButton.Size = new System.Drawing.Size(86, 111); this.SyncButton.TabIndex = 1; this.SyncButton.Text = "\r\n\r\nSync"; this.SyncButton.UseVisualStyleBackColor = true; @@ -255,9 +265,10 @@ this.AddButton.FlatAppearance.BorderSize = 0; this.AddButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.AddButton.Font = new System.Drawing.Font("Segoe MDL2 Assets", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - this.AddButton.Location = new System.Drawing.Point(12, 3); + this.AddButton.Location = new System.Drawing.Point(18, 4); + this.AddButton.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4); this.AddButton.Name = "AddButton"; - this.AddButton.Size = new System.Drawing.Size(57, 74); + this.AddButton.Size = new System.Drawing.Size(86, 111); this.AddButton.TabIndex = 0; this.AddButton.Text = "\r\n\r\nAdd"; this.AddButton.UseVisualStyleBackColor = true; @@ -265,20 +276,20 @@ // // GameLibrary // - this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleDimensions = new System.Drawing.SizeF(144F, 144F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.BackColor = System.Drawing.SystemColors.Window; - this.ClientSize = new System.Drawing.Size(1004, 511); + this.ClientSize = new System.Drawing.Size(1506, 766); this.Controls.Add(this.listView1); this.Controls.Add(this.panel1); this.Controls.Add(this.statusStrip1); this.DoubleBuffered = true; this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Margin = new System.Windows.Forms.Padding(2); - this.MinimumSize = new System.Drawing.Size(1018, 544); + this.MinimumSize = new System.Drawing.Size(1516, 788); this.Name = "GameLibrary"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Game Library"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.GameLibrary_FormClosing); this.Load += new System.EventHandler(this.GameLibrary_Load); this.statusStrip1.ResumeLayout(false); this.statusStrip1.PerformLayout(); diff --git a/OpenSaveCloudClient/GameLibraryForm.cs b/OpenSaveCloudClient/GameLibraryForm.cs index 8d8c9e8..d1382fe 100644 --- a/OpenSaveCloudClient/GameLibraryForm.cs +++ b/OpenSaveCloudClient/GameLibraryForm.cs @@ -55,6 +55,18 @@ namespace OpenSaveCloudClient try { saveManager.DetectChanges(); + this.Invoke((MethodInvoker)delegate { + SetAdminControls(); + AboutButton.Enabled = true; + if (_configuration.GetBoolean("synchronization.at_login", true)) + { + SyncButton_Click(sender, e); + } + else + { + LockCriticalControls(false); + } + }); SetTaskEnded(taskUuid); } catch (Exception e) @@ -62,17 +74,6 @@ namespace OpenSaveCloudClient logManager.AddError(e); SetTaskFailed(taskUuid); } - this.Invoke((MethodInvoker)delegate { - SetAdminControls(); - AboutButton.Enabled = true; - if (_configuration.GetBoolean("synchronization.at_login", true)) - { - SyncButton_Click(sender, e); - } else - { - LockCriticalControls(false); - } - }); } }).Start(); } @@ -453,5 +454,18 @@ namespace OpenSaveCloudClient UserManagementForm form = new(); form.ShowDialog(); } + + private void GameLibrary_FormClosing(object sender, FormClosingEventArgs e) + { + bool busy = (taskManager.TasksInformation.Count(ti => ti.Status == AsyncTaskStatus.Running) > 0); + if (busy) + { + logManager.Cleared -= LogManager_LogCleared; + logManager.NewMessage -= LogManager_NewMessage; + WaitingForm form = new(); + form.ShowDialog(); + taskManager.TaskChanged -= taskManager_TaskChanged; + } + } } } \ No newline at end of file diff --git a/OpenSaveCloudClient/GameLibraryForm.resx b/OpenSaveCloudClient/GameLibraryForm.resx index e4bc6d4..5f3b665 100644 --- a/OpenSaveCloudClient/GameLibraryForm.resx +++ b/OpenSaveCloudClient/GameLibraryForm.resx @@ -65,7 +65,7 @@ AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA7OMCAAJNU0Z0AUkBTAMBAQAB - wAEBAcABAQEIAQEBdgEBBP8BGQEACP8BQgFNATYHAAE2AwABKAMAASABBAIAAXYBAQIAAQEBAAEYBQAB + yAEBAcgBAQEIAQEBdgEBBP8BGQEACP8BQgFNATYHAAE2AwABKAMAASABBAIAAXYBAQIAAQEBAAEYBQAB QAEUARIRAANSA1EBUgFRBVICUQNSAlEDUgJRAVIEUQFSBVEBUg1RAVAIUQFQAlEBUAFRAVADUQFQAVEE UAFRAVABUQJQAVECUAFRAVABUQ5QAU8BUAFPAlADTwZQAU8BUAFPAVADTwNQAU8CUAZPAVABTwFQBU8B UAZPAU4ETwFOBE8BTgVPAU4DTwJOBE8CTgJPAU4BTwFOAU8HTgFPCE4BTwdOAU0GTgJNBE4BTQNOAk0B diff --git a/OpenSaveCloudClient/WaitingForm.Designer.cs b/OpenSaveCloudClient/WaitingForm.Designer.cs new file mode 100644 index 0000000..df74ae8 --- /dev/null +++ b/OpenSaveCloudClient/WaitingForm.Designer.cs @@ -0,0 +1,87 @@ +namespace OpenSaveCloudClient +{ + partial class WaitingForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(WaitingForm)); + this.label1 = new System.Windows.Forms.Label(); + this.LoadingIndicator = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.LoadingIndicator)).BeginInit(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Segoe UI", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.label1.Location = new System.Drawing.Point(103, 40); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(412, 56); + this.label1.TabIndex = 0; + this.label1.Text = "Sorry but some tasks are in progress\r\nBe sure to wait for the tasks to be complet" + + "ed...\r\n"; + // + // LoadingIndicator + // + this.LoadingIndicator.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.LoadingIndicator.Image = ((System.Drawing.Image)(resources.GetObject("LoadingIndicator.Image"))); + this.LoadingIndicator.Location = new System.Drawing.Point(12, 27); + this.LoadingIndicator.Name = "LoadingIndicator"; + this.LoadingIndicator.Size = new System.Drawing.Size(80, 82); + this.LoadingIndicator.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.LoadingIndicator.TabIndex = 8; + this.LoadingIndicator.TabStop = false; + // + // WaitingForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.SystemColors.Window; + this.ClientSize = new System.Drawing.Size(528, 136); + this.Controls.Add(this.LoadingIndicator); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.MaximumSize = new System.Drawing.Size(528, 136); + this.MinimumSize = new System.Drawing.Size(528, 136); + this.Name = "WaitingForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "WaitingForm"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.WaitingForm_FormClosing); + ((System.ComponentModel.ISupportInitialize)(this.LoadingIndicator)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Label label1; + private PictureBox LoadingIndicator; + private System.Windows.Forms.Timer timer1; + } +} \ No newline at end of file diff --git a/OpenSaveCloudClient/WaitingForm.cs b/OpenSaveCloudClient/WaitingForm.cs new file mode 100644 index 0000000..465be53 --- /dev/null +++ b/OpenSaveCloudClient/WaitingForm.cs @@ -0,0 +1,42 @@ +using OpenSaveCloudClient.Core; +using OpenSaveCloudClient.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace OpenSaveCloudClient +{ + public partial class WaitingForm : Form + { + private TaskManager taskManager; + bool busy = true; + public WaitingForm() + { + InitializeComponent(); + taskManager = TaskManager.GetInstance(); + taskManager.TaskChanged += OnTaskChanged; + } + + private void WaitingForm_FormClosing(object sender, FormClosingEventArgs e) + { + e.Cancel = busy; + } + + private void OnTaskChanged(object? sender, TaskChangedEventArgs e) + { + busy = (taskManager.TasksInformation.Count(ti => ti.Status == AsyncTaskStatus.Running) > 0); + if (!busy) + { + this.Invoke((MethodInvoker)delegate { + Close(); + }); + } + } + } +} diff --git a/OpenSaveCloudClient/WaitingForm.resx b/OpenSaveCloudClient/WaitingForm.resx new file mode 100644 index 0000000..f7a69ed --- /dev/null +++ b/OpenSaveCloudClient/WaitingForm.resx @@ -0,0 +1,405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + R0lGODlhyADIAPcBAAAAAAD/AAEBAQICAgMDAwUFBQYGBgcHBwgICAkJCQoKCgwMDA0NDQ4ODg8PDxAQ + EBERERMTExQUFBUVFRYWFhcXFxgYGBoaGhsbGxwcHB0dHR4eHh8fHyEhISIiIiMjIyQkJCUlJSYmJigo + KCkpKSoqKisrKywsLC0tLS8vLzAwMDExMTIyMjMzMzQ0NDY2Njc3Nzg4ODk5OTo6Ojs7Oz09PT4+Pj8/ + P0BAQEFBQUJCQkREREVFRUZGRkdHR0hISElJSUtLS0xMTE1NTU5OTk9PT1BQUFJSUlNTU1VVVVZWVldX + V1hYWFpaWltbW1xcXF1dXV5eXl9fX2FhYWJiYmNjY2RkZGVlZWZmZmhoaGlpaWpqamtra2xsbG1tbW9v + b3BwcHFxcXJycnNzc3R0dHZ2dnd3d3h4eHl5eXp6ent7e319fX5+fn9/f4CAgIGBgYKCgoSEhIWFhYaG + hoeHh4iIiImJiYuLi4yMjI2NjY6Ojo+Pj5CQkJKSkpOTk5SUlJWVlZaWlpeXl5mZmZqampubm5ycnJ2d + nZ6enqCgoKGhoaKioqOjo6SkpKWlpaenp6ioqKmpqaqqqqurq6ysrK2tra+vr7CwsLGxsbKysrOzs7S0 + tLa2tre3t7i4uLm5ubq6uru7u729vb6+vr+/v8DAwMHBwcLCwsTExMXFxcbGxsfHx8jIyMnJycvLy8zM + zM3Nzc7Ozs/Pz9DQ0NLS0tPT09TU1NXV1dbW1tfX19nZ2dra2tvb29zc3N3d3d7e3uDg4OHh4eLi4uPj + 4+Tk5OXl5efn5+jo6Onp6erq6uvr6+zs7O7u7u/v7/Dw8PHx8fLy8vPz8/X19fb29vf39/j4+Pn5+fr6 + +vz8/P39/f7+/v////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + /yH/C05FVFNDQVBFMi4wAwEAAAAh+QQFBAABACwAAAAAyADIAIcAAAAA/wABAQECAgIDAwMFBQUGBgYH + BwcICAgJCQkKCgoMDAwNDQ0ODg4PDw8QEBARERETExMUFBQVFRUWFhYXFxcYGBgaGhobGxscHBwdHR0e + Hh4fHx8hISEiIiIjIyMkJCQlJSUmJiYoKCgpKSkqKiorKyssLCwtLS0vLy8wMDAxMTEyMjIzMzM0NDQ2 + NjY3Nzc4ODg5OTk6Ojo7Ozs9PT0+Pj4/Pz9AQEBBQUFCQkJERERFRUVGRkZHR0dISEhJSUlLS0tMTExN + TU1OTk5PT09QUFBSUlJTU1NVVVVWVlZXV1dYWFhaWlpbW1tcXFxdXV1eXl5fX19hYWFiYmJjY2NkZGRl + ZWVmZmZoaGhpaWlqampra2tsbGxtbW1vb29wcHBxcXFycnJzc3N0dHR2dnZ3d3d4eHh5eXl6enp7e3t9 + fX1+fn5/f3+AgICBgYGCgoKEhISFhYWGhoaHh4eIiIiJiYmLi4uMjIyNjY2Ojo6Pj4+QkJCSkpKTk5OU + lJSVlZWWlpaXl5eZmZmampqbm5ucnJydnZ2enp6goKChoaGioqKjo6OkpKSlpaWnp6eoqKipqamqqqqr + q6usrKytra2vr6+wsLCxsbGysrKzs7O0tLS2tra3t7e4uLi5ubm6urq7u7u9vb2+vr6/v7/AwMDBwcHC + wsLExMTFxcXGxsbHx8fIyMjJycnLy8vMzMzNzc3Ozs7Pz8/Q0NDS0tLT09PU1NTV1dXW1tbX19fZ2dna + 2trb29vc3Nzd3d3e3t7g4ODh4eHi4uLj4+Pk5OTl5eXn5+fo6Ojp6enq6urr6+vs7Ozu7u7v7+/w8PDx + 8fHy8vLz8/P19fX29vb39/f4+Pj5+fn6+vr8/Pz9/f3+/v7///////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////8I/wCxbRNIcKDBgggPKkzIcKHDhhAfSoxIcaLFihgvasxY + cFu2bdo8ghQZ8mPJkSZJqkzJEqXLkzBXvpQZs2XNmTZp6szJE6fPm0B3/uS4sSjRo0aTIl2qtCnRn0KD + 9pQKdWrUq1azVt1KtSvWqkzDOh0rtizZs2Y7avW69itbrm7jtp0Ll27XtGjz4t2rty/TuoDfCpYLV+Bg + u4QPB17Jt7HfbdB0WQIUZooPGR4kLEgAgACAAQQgMBjhgkiTMnw4zaLmuHXexLAR2802y1EZIBcEANit + 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 + 6uuvLxkQEAA7 + + + \ No newline at end of file