Hash for checking upload/download, waiting screen

This commit is contained in:
Aurélie Delhaie
2022-06-16 22:44:53 +02:00
parent 24a04e8c02
commit 2a9fd72e14
17 changed files with 691 additions and 80 deletions

View File

@@ -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("-", "");
}
}
}

View File

@@ -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<string>? 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
{

View File

@@ -11,6 +11,7 @@ namespace OpenSaveCloudClient.Core
{
private static TaskManager instance;
private static LogManager logManager;
private readonly System.Timers.Timer timer;
private readonly Dictionary<string, AsyncTaskInformation> _tasks;
@@ -20,6 +21,7 @@ namespace OpenSaveCloudClient.Core
private TaskManager()
{
logManager = LogManager.GetInstance();
_tasks = new Dictionary<string, AsyncTaskInformation>();
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<string> toDelete = new();
foreach (KeyValuePair<string, AsyncTaskInformation> 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
{

View File

@@ -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();

View File

@@ -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;
}
}
}
}

View File

@@ -65,7 +65,7 @@
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA7OMCAAJNU0Z0AUkBTAMBAQAB
wAEBAcABAQEIAQEBdgEBBP8BGQEACP8BQgFNATYHAAE2AwABKAMAASABBAIAAXYBAQIAAQEBAAEYBQAB
yAEBAcgBAQEIAQEBdgEBBP8BGQEACP8BQgFNATYHAAE2AwABKAMAASABBAIAAXYBAQIAAQEBAAEYBQAB
QAEUARIRAANSA1EBUgFRBVICUQNSAlEDUgJRAVIEUQFSBVEBUg1RAVAIUQFQAlEBUAFRAVADUQFQAVEE
UAFRAVABUQJQAVECUAFRAVABUQ5QAU8BUAFPAlADTwZQAU8BUAFPAVADTwNQAU8CUAZPAVABTwFQBU8B
UAZPAU4ETwFOBE8BTgVPAU4DTwJOBE8CTgJPAU4BTwFOAU8HTgFPCE4BTwdOAU0GTgJNBE4BTQNOAk0B

View File

@@ -0,0 +1,87 @@
namespace OpenSaveCloudClient
{
partial class WaitingForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
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;
}
}

View File

@@ -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();
});
}
}
}
}

View File

@@ -0,0 +1,405 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="LoadingIndicator.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
</root>