Force downlaod/upload, download dialog
This commit is contained in:
@@ -127,7 +127,7 @@ namespace OpenSaveCloudClient
|
||||
{
|
||||
NameBox.Text = LocationBox.Text.Split(Path.DirectorySeparatorChar).Last();
|
||||
}
|
||||
if (NameWarningLabel.Enabled)
|
||||
if (NameWarningLabel.Visible)
|
||||
{
|
||||
if (MessageBox.Show(
|
||||
"There is already a game with this name in the library. Would you like to add it anyway?",
|
||||
@@ -135,7 +135,7 @@ namespace OpenSaveCloudClient
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question) == DialogResult.No) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = new GameSave(NameBox.Text, "", LocationBox.Text, null, 0);
|
||||
DialogResult = DialogResult.OK;
|
||||
|
||||
83
OpenSaveCloudClient/Core/HashTool.cs
Normal file
83
OpenSaveCloudClient/Core/HashTool.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OpenSaveCloudClient.Core
|
||||
{
|
||||
internal class HashTool
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// method <c>HashDirectory</c> walk through a directory and make a hash of all the files
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the folder</param>
|
||||
/// <returns>The hash of the folder</returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public static byte[]? HashDirectory(string path)
|
||||
{
|
||||
DirectoryInfo dir = new(path);
|
||||
using HashAlgorithm? sha = HashAlgorithm.Create("SHA-512");
|
||||
if (sha == null)
|
||||
{
|
||||
throw new InvalidOperationException("HashDirectory: SHA-512 algorithm does not exist");
|
||||
}
|
||||
using (var stream = new CryptoStream(Stream.Null, sha, CryptoStreamMode.Write))
|
||||
{
|
||||
using var writer = new BinaryWriter(stream);
|
||||
FileSystemInfo[] infos = dir.GetFileSystemInfos();
|
||||
Array.Sort(infos, (a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
||||
foreach (FileSystemInfo info in infos)
|
||||
{
|
||||
writer.Write(info.Name);
|
||||
if ((info.Attributes & FileAttributes.Directory) == 0)
|
||||
{
|
||||
byte[]? hash = HashFile(info as FileInfo);
|
||||
if (hash == null)
|
||||
{
|
||||
throw new InvalidOperationException("HashDirectory: hash of the file is null");
|
||||
}
|
||||
writer.Write((byte)'F');
|
||||
writer.Write(hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[]? hash = HashDirectory(info.FullName);
|
||||
if (hash == null)
|
||||
{
|
||||
throw new InvalidOperationException("HashDirectory: hash of the directory is null");
|
||||
}
|
||||
writer.Write((byte)'D');
|
||||
writer.Write(hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sha.Hash;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>HashFile</c> make a hash SHA-512 of a file
|
||||
/// </summary>
|
||||
/// <param name="fileInfo">The file information</param>
|
||||
/// <returns>The file's hash</returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
private static byte[]? HashFile(FileInfo? fileInfo)
|
||||
{
|
||||
if (fileInfo == null)
|
||||
{
|
||||
throw new Exception("HashFile: invalid file information");
|
||||
}
|
||||
using HashAlgorithm? sha = HashAlgorithm.Create("SHA-512");
|
||||
if (sha == null)
|
||||
{
|
||||
throw new InvalidOperationException("HashFile: SHA-512 algorithm does not exist");
|
||||
}
|
||||
using var inputStream = fileInfo.OpenRead();
|
||||
return sha.ComputeHash(inputStream);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -27,16 +27,21 @@ namespace OpenSaveCloudClient.Core
|
||||
}
|
||||
|
||||
public void AddError(Exception ex)
|
||||
{
|
||||
AddError(ex.Message);
|
||||
}
|
||||
|
||||
public void AddError(string message)
|
||||
{
|
||||
Log log = new()
|
||||
{
|
||||
Message = ex.Message,
|
||||
Message = message,
|
||||
Severity = LogSeverity.Error,
|
||||
};
|
||||
messages.Add(log);
|
||||
NewMessageEventArgs args = new()
|
||||
{
|
||||
Message = ex.Message,
|
||||
Message = message,
|
||||
Severity = LogSeverity.Error,
|
||||
};
|
||||
OnNewMessage(args);
|
||||
|
||||
@@ -11,6 +11,10 @@ using System.IO.Compression;
|
||||
|
||||
namespace OpenSaveCloudClient.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is a connector to the remote Open Save Cloud server, it contains all the function that are mapped to the server endpoint
|
||||
/// This is a singleton, to get the instance, call <c>GetInstance()</c>
|
||||
/// </summary>
|
||||
public class ServerConnector
|
||||
{
|
||||
private static ServerConnector? instance;
|
||||
@@ -51,6 +55,11 @@ namespace OpenSaveCloudClient.Core
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>BindNewServer</c> set the hostname (or ip) and the port of the server and try to connect
|
||||
/// </summary>
|
||||
/// <param name="host">hostname or IP of the server</param>
|
||||
/// <param name="port">port of the server</param>
|
||||
public void BindNewServer(string host, int port)
|
||||
{
|
||||
Logout();
|
||||
@@ -64,6 +73,11 @@ namespace OpenSaveCloudClient.Core
|
||||
GetServerInformation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>Login</c> connect a user and save the token to <c>token</c>
|
||||
/// </summary>
|
||||
/// <param name="username">Username of the user</param>
|
||||
/// <param name="password">Password of the user</param>
|
||||
public void Login(string username, string password)
|
||||
{
|
||||
logManager.AddInformation("Loging in to the server");
|
||||
@@ -102,6 +116,9 @@ namespace OpenSaveCloudClient.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>Reconnect</c> try to reconnect with the token, hostname and port saved in the configuration file
|
||||
/// </summary>
|
||||
public void Reconnect()
|
||||
{
|
||||
string? uuidTask = null;
|
||||
@@ -147,6 +164,9 @@ namespace OpenSaveCloudClient.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>Logout</c> disconnect the current user and remove all the server's information of the config file
|
||||
/// </summary>
|
||||
public void Logout()
|
||||
{
|
||||
serverInformation = null;
|
||||
@@ -159,6 +179,11 @@ namespace OpenSaveCloudClient.Core
|
||||
configuration.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>CreateGame</c> create a new game entry in the server database
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the game</param>
|
||||
/// <returns>The game that was created by the server</returns>
|
||||
public Game? CreateGame(string name)
|
||||
{
|
||||
logManager.AddInformation("Creating game to server database");
|
||||
@@ -178,7 +203,7 @@ namespace OpenSaveCloudClient.Core
|
||||
return JsonSerializer.Deserialize<Game>(responseText);
|
||||
} else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
logManager.AddError(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString()));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
@@ -190,99 +215,157 @@ namespace OpenSaveCloudClient.Core
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>Synchronize</c> is the method that upload and download the files from the server
|
||||
/// If there is conflict, a warning is generated and the files are keep untouched
|
||||
/// </summary>
|
||||
public async void Synchronize()
|
||||
{
|
||||
string appdata = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "osc");
|
||||
string cachePath = Path.Combine(appdata, "cache");
|
||||
logManager.AddInformation("Starting synchronization");
|
||||
List<GameSave> games = saveManager.Saves;
|
||||
string uuidTask = taskManager.StartTask("Synchronizing games", true, games.Count);
|
||||
List<GameSave> toUpload = new();
|
||||
List<GameSave> toDownload = new();
|
||||
foreach (GameSave game in games)
|
||||
foreach (GameSave localCopy in games)
|
||||
{
|
||||
try
|
||||
{
|
||||
game.Archive();
|
||||
Game? g = GetGameInfoByID(game.Id);
|
||||
if (g != null)
|
||||
// Get the current information that are stored in the server
|
||||
Game? remoteCopy = GetGameInfoByID(localCopy.Id);
|
||||
if (remoteCopy == null)
|
||||
{
|
||||
if (g.Available)
|
||||
{
|
||||
if (g.Revision > game.Revision)
|
||||
{
|
||||
toDownload.Add(game);
|
||||
}
|
||||
else if (g.Revision < game.Revision)
|
||||
{
|
||||
logManager.AddWarning(String.Format("Revision are the same, maybe uploaded by another computer ({0})", game.Name));
|
||||
logManager.AddInformation("To resolve this conflict, force download or force upload from the game detail screen");
|
||||
}
|
||||
else
|
||||
{
|
||||
toUpload.Add(game);
|
||||
}
|
||||
} else
|
||||
{
|
||||
logManager.AddInformation(String.Format("First upload of '{0}'", game.Name));
|
||||
toUpload.Add(game);
|
||||
}
|
||||
} else
|
||||
{
|
||||
logManager.AddWarning(String.Format("'{0}' is not found on this server, force upload it from the game detail screen", game.Name));
|
||||
logManager.AddWarning(String.Format("'{0}' is not found on this server, force upload it from the game detail screen", localCopy.Name));
|
||||
continue;
|
||||
}
|
||||
} catch (Exception ex)
|
||||
|
||||
// Check if available on the server
|
||||
if (!remoteCopy.Available)
|
||||
{
|
||||
logManager.AddInformation(String.Format("'{0}' does not exist in the server", localCopy.Name));
|
||||
localCopy.DetectChanges();
|
||||
toUpload.Add(localCopy);
|
||||
}
|
||||
else if (localCopy.DetectChanges() || !localCopy.Synced)
|
||||
{
|
||||
// Create an archive of the folder
|
||||
localCopy.Archive();
|
||||
|
||||
// Upload only if the revision is the same
|
||||
if (remoteCopy.Revision != localCopy.Revision)
|
||||
{
|
||||
logManager.AddWarning(String.Format("There revision of the local copy is not equal with the copy on the server ({0})", localCopy.Name));
|
||||
logManager.AddInformation("To resolve this conflict, force download or force upload from the game detail screen");
|
||||
continue;
|
||||
}
|
||||
toUpload.Add(localCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (remoteCopy.Revision > localCopy.Revision)
|
||||
{
|
||||
toDownload.Add(localCopy);
|
||||
}
|
||||
else if (remoteCopy.Revision < localCopy.Revision)
|
||||
{
|
||||
logManager.AddWarning(String.Format("There revision of the local copy is not equal with the copy on the server ({0})", localCopy.Name));
|
||||
logManager.AddInformation("To resolve this conflict, force download or force upload from the game detail screen");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
}
|
||||
taskManager.UpdateTaskProgress(uuidTask, 1);
|
||||
}
|
||||
|
||||
// Upload files
|
||||
UploadGames(toUpload);
|
||||
|
||||
// Download new version of files
|
||||
await DownloadGamesAsync(toDownload);
|
||||
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>UploadGames</c> upload the game saves to the server
|
||||
/// </summary>
|
||||
/// <param name="toUpload">A list of GameSaves</param>
|
||||
public void UploadGames(List<GameSave> toUpload)
|
||||
{
|
||||
string cachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "osc", "cache");
|
||||
foreach (GameSave game in toUpload)
|
||||
{
|
||||
GameUploadToken? gut = LockGameToUpload(game.Id);
|
||||
if (gut != null)
|
||||
{
|
||||
string archivePath = Path.Combine(cachePath, game.Uuid + ".bin");
|
||||
UploadSave(gut.UploadToken, archivePath);
|
||||
UpdateCache(game.Id, game);
|
||||
}
|
||||
}
|
||||
foreach (GameSave game in toDownload)
|
||||
{
|
||||
GameUploadToken? gut = LockGameToUpload(game.Id);
|
||||
if (gut != null)
|
||||
{
|
||||
string archivePath = Path.Combine(cachePath, game.Uuid + ".bin");
|
||||
if (await DownloadSaveAsync(gut.UploadToken, archivePath, game.FolderPath))
|
||||
if (UploadSave(gut.UploadToken, archivePath, game.CurrentHash))
|
||||
{
|
||||
UpdateCache(game.Id, game);
|
||||
game.UpdateHash();
|
||||
UpdateCache(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
saveManager.Save();
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
}
|
||||
|
||||
public Game? GetGameInfoByID(int gameId)
|
||||
/// <summary>
|
||||
/// method <c>DownloadGamesAsync</c> download the game saves from the server
|
||||
/// This method is async because of <c>DownloadSaveAsync</c> that are async too
|
||||
/// </summary>
|
||||
/// <param name="toDownload">A list of GameSaves</param>
|
||||
/// <returns>A task</returns>
|
||||
public async Task DownloadGamesAsync(List<GameSave> toDownload)
|
||||
{
|
||||
string cachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "osc", "cache");
|
||||
string archivePath;
|
||||
GameUploadToken? gut;
|
||||
foreach (GameSave game in toDownload)
|
||||
{
|
||||
gut = LockGameToUpload(game.Id);
|
||||
if (gut != null)
|
||||
{
|
||||
archivePath = Path.Combine(cachePath, game.Uuid + ".bin");
|
||||
if (await DownloadSaveAsync(gut.UploadToken, archivePath, game.FolderPath))
|
||||
{
|
||||
game.DetectChanges();
|
||||
game.UpdateHash();
|
||||
UpdateCache(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
saveManager.Save();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>GetGameInfoByID</c> get the game save information from the server
|
||||
/// </summary>
|
||||
/// <param name="gameId">A game id</param>
|
||||
/// <returns>A remote object of a game save</returns>
|
||||
public Game? GetGameInfoByID(long gameId)
|
||||
{
|
||||
logManager.AddInformation("Getting game information from the server database");
|
||||
string uuidTask = taskManager.StartTask("Getting game information", true, 1);
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
HttpResponseMessage response = client.GetAsync(string.Format("{0}:{1}/api/v1/game/info/{2}", host, port, gameId)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return JsonSerializer.Deserialize<Game>(responseText);
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
HttpResponseMessage response = client.GetAsync(string.Format("{0}:{1}/api/v1/game/info/{2}", host, port, gameId)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return JsonSerializer.Deserialize<Game>(responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString()));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -292,7 +375,49 @@ namespace OpenSaveCloudClient.Core
|
||||
return null;
|
||||
}
|
||||
|
||||
public void UploadSave(string uploadToken, string filePath)
|
||||
/// <summary>
|
||||
/// method <c>GetGamesInfo</c> get all the save registered on the server of the current user
|
||||
/// </summary>
|
||||
/// <param name="gameId">A game id</param>
|
||||
/// <returns>A list of remote object of a game save</returns>
|
||||
public List<Game>? GetGamesInfo()
|
||||
{
|
||||
logManager.AddInformation("Getting game information from the server database");
|
||||
string uuidTask = taskManager.StartTask("Getting game information", true, 1);
|
||||
try
|
||||
{
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
HttpResponseMessage response = client.GetAsync(string.Format("{0}:{1}/api/v1/game/all", host, port)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return JsonSerializer.Deserialize<List<Game>>(responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString()));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>UploadSave</c> upload a file to the server
|
||||
/// </summary>
|
||||
/// <param name="uploadToken">The Lock token that a provided by the server</param>
|
||||
/// <param name="filePath">The path of the file</param>
|
||||
/// <param name="newHash">The new hash of the folder</param>
|
||||
public bool UploadSave(string uploadToken, string filePath, string newHash)
|
||||
{
|
||||
logManager.AddInformation("Uploading save");
|
||||
string uuidTask = taskManager.StartTask("Uploading", true, 1);
|
||||
@@ -304,20 +429,23 @@ namespace OpenSaveCloudClient.Core
|
||||
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
|
||||
multipartFormContent.Add(fileStreamContent, name: "file", fileName: "file.bin");
|
||||
|
||||
HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
client.DefaultRequestHeaders.Add("X-Upload-Key", uploadToken);
|
||||
HttpResponseMessage response = client.PostAsync(string.Format("{0}:{1}/api/v1/game/upload", host, port), multipartFormContent).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return;
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
client.DefaultRequestHeaders.Add("X-Upload-Key", uploadToken);
|
||||
client.DefaultRequestHeaders.Add("X-Game-Save-Hash", newHash);
|
||||
HttpResponseMessage response = client.PostAsync(string.Format("{0}:{1}/api/v1/game/upload", host, port), multipartFormContent).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString()));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -327,36 +455,46 @@ namespace OpenSaveCloudClient.Core
|
||||
{
|
||||
stream.Close();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="uploadToken">The Lock token that a provided by the server</param>
|
||||
/// <param name="filePath">The path where the downloaded archive will be stored</param>
|
||||
/// <param name="unzipPath">The path is stored the save</param>
|
||||
/// <returns>If the save is successfully downloaded and unpacked</returns>
|
||||
public async Task<bool> DownloadSaveAsync(string uploadToken, string filePath, string unzipPath)
|
||||
{
|
||||
logManager.AddInformation("Downloading save");
|
||||
string uuidTask = taskManager.StartTask("Downloading", true, 1);
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
client.DefaultRequestHeaders.Add("X-Upload-Key", uploadToken);
|
||||
HttpResponseMessage response = client.GetAsync(string.Format("{0}:{1}/api/v1/game/download", host, port)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
using (var fs = new FileStream(filePath, FileMode.Create))
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
client.DefaultRequestHeaders.Add("X-Upload-Key", uploadToken);
|
||||
HttpResponseMessage response = client.GetAsync(string.Format("{0}:{1}/api/v1/game/download", host, port)).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
await response.Content.CopyToAsync(fs);
|
||||
using (var fs = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await response.Content.CopyToAsync(fs);
|
||||
}
|
||||
if (Directory.Exists(unzipPath))
|
||||
{
|
||||
Directory.Delete(unzipPath, true);
|
||||
}
|
||||
ZipFile.ExtractToDirectory(filePath, unzipPath);
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return true;
|
||||
}
|
||||
if (Directory.Exists(unzipPath))
|
||||
else
|
||||
{
|
||||
Directory.Delete(unzipPath, true);
|
||||
logManager.AddError(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString()));
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
ZipFile.ExtractToDirectory(filePath, unzipPath);
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -367,47 +505,58 @@ namespace OpenSaveCloudClient.Core
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UpdateCache(int gameId, GameSave gameSave)
|
||||
/// <summary>
|
||||
/// method <c>UpdateCache</c> update the GameSave object with the server data
|
||||
/// </summary>
|
||||
/// <param name="gameSave">A GameSave object</param>
|
||||
private void UpdateCache(GameSave gameSave)
|
||||
{
|
||||
string uuidTask = taskManager.StartTask("Updating cache", true, 1);
|
||||
Game? game = GetGameInfoByID(gameId);
|
||||
Game? game = GetGameInfoByID(gameSave.Id);
|
||||
if (game != null)
|
||||
{
|
||||
gameSave.Revision = game.Revision;
|
||||
gameSave.LocalOnly = false;
|
||||
gameSave.Synced = true;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(new Exception("Failed to get game information"));
|
||||
logManager.AddError("Failed to get game information");
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
|
||||
private GameUploadToken? LockGameToUpload(int gameId)
|
||||
/// <summary>
|
||||
/// method <c>LockGameToUpload</c> lock a game save on the server
|
||||
/// This method is useful to avoid competing uploads/downloads
|
||||
/// </summary>
|
||||
/// <param name="gameId">A game id</param>
|
||||
/// <returns>A token to give to the upload/download method</returns>
|
||||
private GameUploadToken? LockGameToUpload(long gameId)
|
||||
{
|
||||
logManager.AddInformation("Locking game in the server");
|
||||
string uuidTask = taskManager.StartTask("Locking game", true, 1);
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
string json = JsonSerializer.Serialize(new UploadGameInfo { GameId = gameId });
|
||||
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
HttpResponseMessage response = client.PostAsync(string.Format("{0}:{1}/api/v1/game/upload/init", host, port), content).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
logManager.AddInformation("Game locked");
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return JsonSerializer.Deserialize<GameUploadToken>(responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(new Exception(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString())));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
string json = JsonSerializer.Serialize(new UploadGameInfo { GameId = gameId });
|
||||
HttpContent content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
client.DefaultRequestHeaders.Add("Authorization", "bearer " + token);
|
||||
HttpResponseMessage response = client.PostAsync(string.Format("{0}:{1}/api/v1/game/upload/init", host, port), content).Result;
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
logManager.AddInformation("Game locked");
|
||||
string responseText = response.Content.ReadAsStringAsync().Result;
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Ended);
|
||||
return JsonSerializer.Deserialize<GameUploadToken>(responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
logManager.AddError(String.Format("Received HTTP Status {0} from the server", response.StatusCode.ToString()));
|
||||
}
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -417,6 +566,10 @@ namespace OpenSaveCloudClient.Core
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>ReloadFromConfiguration</c> load the server information (host, port and token) from the configuration file
|
||||
/// </summary>
|
||||
/// <returns>The configuration is valid</returns>
|
||||
private bool ReloadFromConfiguration()
|
||||
{
|
||||
string newHost = configuration.GetString("authentication.host", "");
|
||||
@@ -447,6 +600,9 @@ namespace OpenSaveCloudClient.Core
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>GetServerInformation</c> get information about the connected server
|
||||
/// </summary>
|
||||
private void GetServerInformation()
|
||||
{
|
||||
logManager.AddInformation("Getting server information");
|
||||
@@ -475,6 +631,9 @@ namespace OpenSaveCloudClient.Core
|
||||
taskManager.UpdateTaskStatus(uuidTask, AsyncTaskStatus.Failed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// method <c>SaveToConfig</c> save the server connection information to the configuration file
|
||||
/// </summary>
|
||||
private void SaveToConfig()
|
||||
{
|
||||
configuration.SetValue("authentication.host", host);
|
||||
@@ -482,5 +641,6 @@ namespace OpenSaveCloudClient.Core
|
||||
configuration.SetValue("authentication.token", token);
|
||||
configuration.Flush();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
194
OpenSaveCloudClient/DetailForm.Designer.cs
generated
Normal file
194
OpenSaveCloudClient/DetailForm.Designer.cs
generated
Normal file
@@ -0,0 +1,194 @@
|
||||
namespace OpenSaveCloudClient
|
||||
{
|
||||
partial class DetailForm
|
||||
{
|
||||
/// <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()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DetailForm));
|
||||
this.TitleLabel = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.RevisionLabel = new System.Windows.Forms.Label();
|
||||
this.PathLabel = new System.Windows.Forms.Label();
|
||||
this.SyncedLabel = new System.Windows.Forms.Label();
|
||||
this.label8 = new System.Windows.Forms.Label();
|
||||
this.ChecksumBox = new System.Windows.Forms.TextBox();
|
||||
this.UploadButton = new System.Windows.Forms.Button();
|
||||
this.DownloadButton = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// TitleLabel
|
||||
//
|
||||
this.TitleLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.TitleLabel.AutoEllipsis = true;
|
||||
this.TitleLabel.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||
this.TitleLabel.ForeColor = System.Drawing.SystemColors.Highlight;
|
||||
this.TitleLabel.Location = new System.Drawing.Point(12, 9);
|
||||
this.TitleLabel.Name = "TitleLabel";
|
||||
this.TitleLabel.Size = new System.Drawing.Size(941, 48);
|
||||
this.TitleLabel.TabIndex = 0;
|
||||
this.TitleLabel.Text = "_";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(12, 95);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(77, 25);
|
||||
this.label2.TabIndex = 1;
|
||||
this.label2.Text = "Revision";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(12, 120);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(46, 25);
|
||||
this.label3.TabIndex = 2;
|
||||
this.label3.Text = "Path";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(12, 145);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(68, 25);
|
||||
this.label4.TabIndex = 3;
|
||||
this.label4.Text = "Synced";
|
||||
//
|
||||
// RevisionLabel
|
||||
//
|
||||
this.RevisionLabel.AutoSize = true;
|
||||
this.RevisionLabel.Location = new System.Drawing.Point(125, 95);
|
||||
this.RevisionLabel.Name = "RevisionLabel";
|
||||
this.RevisionLabel.Size = new System.Drawing.Size(16, 25);
|
||||
this.RevisionLabel.TabIndex = 4;
|
||||
this.RevisionLabel.Text = ".";
|
||||
//
|
||||
// PathLabel
|
||||
//
|
||||
this.PathLabel.AutoSize = true;
|
||||
this.PathLabel.Location = new System.Drawing.Point(125, 120);
|
||||
this.PathLabel.Name = "PathLabel";
|
||||
this.PathLabel.Size = new System.Drawing.Size(16, 25);
|
||||
this.PathLabel.TabIndex = 5;
|
||||
this.PathLabel.Text = ".";
|
||||
//
|
||||
// SyncedLabel
|
||||
//
|
||||
this.SyncedLabel.AutoSize = true;
|
||||
this.SyncedLabel.Location = new System.Drawing.Point(125, 145);
|
||||
this.SyncedLabel.Name = "SyncedLabel";
|
||||
this.SyncedLabel.Size = new System.Drawing.Size(16, 25);
|
||||
this.SyncedLabel.TabIndex = 6;
|
||||
this.SyncedLabel.Text = ".";
|
||||
//
|
||||
// label8
|
||||
//
|
||||
this.label8.AutoSize = true;
|
||||
this.label8.Location = new System.Drawing.Point(12, 176);
|
||||
this.label8.Name = "label8";
|
||||
this.label8.Size = new System.Drawing.Size(93, 25);
|
||||
this.label8.TabIndex = 7;
|
||||
this.label8.Text = "Checksum";
|
||||
//
|
||||
// ChecksumBox
|
||||
//
|
||||
this.ChecksumBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.ChecksumBox.Location = new System.Drawing.Point(125, 173);
|
||||
this.ChecksumBox.Name = "ChecksumBox";
|
||||
this.ChecksumBox.ReadOnly = true;
|
||||
this.ChecksumBox.Size = new System.Drawing.Size(828, 31);
|
||||
this.ChecksumBox.TabIndex = 8;
|
||||
//
|
||||
// UploadButton
|
||||
//
|
||||
this.UploadButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.UploadButton.Location = new System.Drawing.Point(12, 244);
|
||||
this.UploadButton.Name = "UploadButton";
|
||||
this.UploadButton.Size = new System.Drawing.Size(112, 34);
|
||||
this.UploadButton.TabIndex = 9;
|
||||
this.UploadButton.Text = "Upload";
|
||||
this.UploadButton.UseVisualStyleBackColor = true;
|
||||
this.UploadButton.Click += new System.EventHandler(this.UploadButton_Click);
|
||||
//
|
||||
// DownloadButton
|
||||
//
|
||||
this.DownloadButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.DownloadButton.Location = new System.Drawing.Point(130, 244);
|
||||
this.DownloadButton.Name = "DownloadButton";
|
||||
this.DownloadButton.Size = new System.Drawing.Size(112, 34);
|
||||
this.DownloadButton.TabIndex = 10;
|
||||
this.DownloadButton.Text = "Download";
|
||||
this.DownloadButton.UseVisualStyleBackColor = true;
|
||||
this.DownloadButton.Click += new System.EventHandler(this.DownloadButton_Click);
|
||||
//
|
||||
// DetailForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(965, 290);
|
||||
this.Controls.Add(this.DownloadButton);
|
||||
this.Controls.Add(this.UploadButton);
|
||||
this.Controls.Add(this.ChecksumBox);
|
||||
this.Controls.Add(this.label8);
|
||||
this.Controls.Add(this.SyncedLabel);
|
||||
this.Controls.Add(this.PathLabel);
|
||||
this.Controls.Add(this.RevisionLabel);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.TitleLabel);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MinimumSize = new System.Drawing.Size(987, 346);
|
||||
this.Name = "DetailForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Detail of";
|
||||
this.Load += new System.EventHandler(this.DetailForm_Load);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Label TitleLabel;
|
||||
private Label label2;
|
||||
private Label label3;
|
||||
private Label label4;
|
||||
private Label RevisionLabel;
|
||||
private Label PathLabel;
|
||||
private Label SyncedLabel;
|
||||
private Label label8;
|
||||
private TextBox ChecksumBox;
|
||||
private Button UploadButton;
|
||||
private Button DownloadButton;
|
||||
}
|
||||
}
|
||||
75
OpenSaveCloudClient/DetailForm.cs
Normal file
75
OpenSaveCloudClient/DetailForm.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
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 enum ForcedSyncResult
|
||||
{
|
||||
Upload,
|
||||
Download
|
||||
}
|
||||
|
||||
public partial class DetailForm : Form
|
||||
{
|
||||
|
||||
private ForcedSyncResult? result;
|
||||
private GameSave gameSave;
|
||||
|
||||
public ForcedSyncResult? Result { get { return result; } }
|
||||
|
||||
public DetailForm(GameSave gameSave)
|
||||
{
|
||||
InitializeComponent();
|
||||
this.gameSave = gameSave;
|
||||
}
|
||||
|
||||
private void UploadButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (MessageBox.Show(
|
||||
"Forcing upload will overwrite the save on the server, do you really want to forcing the upload of the save?",
|
||||
"Warning: Forcing upload",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning) == DialogResult.No)
|
||||
{
|
||||
return;
|
||||
}
|
||||
result = ForcedSyncResult.Upload;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void DetailForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
TitleLabel.Text = gameSave.Name;
|
||||
Text = "Detail of " + gameSave.Name;
|
||||
RevisionLabel.Text = Convert.ToString(gameSave.Revision);
|
||||
PathLabel.Text = gameSave.FolderPath;
|
||||
SyncedLabel.Text = gameSave.Synced ? "Yes" : "No";
|
||||
ChecksumBox.Text = gameSave.CurrentHash;
|
||||
}
|
||||
|
||||
private void DownloadButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (MessageBox.Show(
|
||||
"Forcing download will overwrite the local save, do you really want to forcing the download of the save?",
|
||||
"Warning: Forcing download",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Warning) == DialogResult.No)
|
||||
{
|
||||
return;
|
||||
}
|
||||
result = ForcedSyncResult.Download;
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
2280
OpenSaveCloudClient/DetailForm.resx
Normal file
2280
OpenSaveCloudClient/DetailForm.resx
Normal file
File diff suppressed because it is too large
Load Diff
184
OpenSaveCloudClient/DownloadGameForm.Designer.cs
generated
Normal file
184
OpenSaveCloudClient/DownloadGameForm.Designer.cs
generated
Normal file
@@ -0,0 +1,184 @@
|
||||
namespace OpenSaveCloudClient
|
||||
{
|
||||
partial class DownloadGameForm
|
||||
{
|
||||
/// <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()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DownloadGameForm));
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.LoadingIndicator = new System.Windows.Forms.PictureBox();
|
||||
this.groupBox1 = new System.Windows.Forms.GroupBox();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.pathButton = new System.Windows.Forms.Button();
|
||||
this.LocationBox = new System.Windows.Forms.TextBox();
|
||||
this.DownloadButton = new System.Windows.Forms.Button();
|
||||
this.RemoteList = new System.Windows.Forms.ListView();
|
||||
this.GameName = new System.Windows.Forms.ColumnHeader();
|
||||
((System.ComponentModel.ISupportInitialize)(this.LoadingIndicator)).BeginInit();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
//
|
||||
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.SystemColors.Highlight;
|
||||
this.label1.Location = new System.Drawing.Point(12, 9);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(387, 48);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "Download a game save";
|
||||
//
|
||||
// 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(854, 12);
|
||||
this.LoadingIndicator.Name = "LoadingIndicator";
|
||||
this.LoadingIndicator.Size = new System.Drawing.Size(48, 48);
|
||||
this.LoadingIndicator.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
|
||||
this.LoadingIndicator.TabIndex = 7;
|
||||
this.LoadingIndicator.TabStop = false;
|
||||
//
|
||||
// groupBox1
|
||||
//
|
||||
this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.groupBox1.Controls.Add(this.label3);
|
||||
this.groupBox1.Controls.Add(this.pathButton);
|
||||
this.groupBox1.Controls.Add(this.LocationBox);
|
||||
this.groupBox1.Location = new System.Drawing.Point(396, 83);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Size = new System.Drawing.Size(506, 516);
|
||||
this.groupBox1.TabIndex = 8;
|
||||
this.groupBox1.TabStop = false;
|
||||
this.groupBox1.Text = "Set up the save";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(6, 76);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(311, 25);
|
||||
this.label3.TabIndex = 7;
|
||||
this.label3.Text = "Where will the save folder be located?";
|
||||
//
|
||||
// pathButton
|
||||
//
|
||||
this.pathButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.pathButton.Enabled = false;
|
||||
this.pathButton.Location = new System.Drawing.Point(453, 101);
|
||||
this.pathButton.Name = "pathButton";
|
||||
this.pathButton.Size = new System.Drawing.Size(47, 34);
|
||||
this.pathButton.TabIndex = 6;
|
||||
this.pathButton.Text = "...";
|
||||
this.pathButton.UseVisualStyleBackColor = true;
|
||||
this.pathButton.Click += new System.EventHandler(this.pathButton_Click);
|
||||
//
|
||||
// LocationBox
|
||||
//
|
||||
this.LocationBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.LocationBox.Enabled = false;
|
||||
this.LocationBox.Location = new System.Drawing.Point(6, 104);
|
||||
this.LocationBox.Name = "LocationBox";
|
||||
this.LocationBox.Size = new System.Drawing.Size(441, 31);
|
||||
this.LocationBox.TabIndex = 5;
|
||||
this.LocationBox.TextChanged += new System.EventHandler(this.LocationBox_TextChanged);
|
||||
//
|
||||
// DownloadButton
|
||||
//
|
||||
this.DownloadButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.DownloadButton.Enabled = false;
|
||||
this.DownloadButton.Location = new System.Drawing.Point(790, 605);
|
||||
this.DownloadButton.Name = "DownloadButton";
|
||||
this.DownloadButton.Size = new System.Drawing.Size(112, 34);
|
||||
this.DownloadButton.TabIndex = 9;
|
||||
this.DownloadButton.Text = "Download";
|
||||
this.DownloadButton.UseVisualStyleBackColor = true;
|
||||
this.DownloadButton.Click += new System.EventHandler(this.DownloadButton_Click);
|
||||
//
|
||||
// RemoteList
|
||||
//
|
||||
this.RemoteList.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.RemoteList.CheckBoxes = true;
|
||||
this.RemoteList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.GameName});
|
||||
this.RemoteList.Location = new System.Drawing.Point(12, 83);
|
||||
this.RemoteList.Name = "RemoteList";
|
||||
this.RemoteList.Size = new System.Drawing.Size(378, 516);
|
||||
this.RemoteList.TabIndex = 10;
|
||||
this.RemoteList.UseCompatibleStateImageBehavior = false;
|
||||
this.RemoteList.View = System.Windows.Forms.View.Details;
|
||||
this.RemoteList.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.RemoteList_ItemChecked);
|
||||
this.RemoteList.SelectedIndexChanged += new System.EventHandler(this.RemoteList_SelectedIndexChanged);
|
||||
//
|
||||
// GameName
|
||||
//
|
||||
this.GameName.Text = "Game name";
|
||||
this.GameName.Width = 373;
|
||||
//
|
||||
// DownloadGameForm
|
||||
//
|
||||
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(914, 651);
|
||||
this.Controls.Add(this.RemoteList);
|
||||
this.Controls.Add(this.DownloadButton);
|
||||
this.Controls.Add(this.groupBox1);
|
||||
this.Controls.Add(this.LoadingIndicator);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.MinimumSize = new System.Drawing.Size(936, 707);
|
||||
this.Name = "DownloadGameForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
|
||||
this.Text = "Download a game save";
|
||||
this.Load += new System.EventHandler(this.DownloadGameForm_Load);
|
||||
((System.ComponentModel.ISupportInitialize)(this.LoadingIndicator)).EndInit();
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Label label1;
|
||||
private PictureBox LoadingIndicator;
|
||||
private GroupBox groupBox1;
|
||||
private Button DownloadButton;
|
||||
private ListView RemoteList;
|
||||
private ColumnHeader GameName;
|
||||
private Label label3;
|
||||
private Button pathButton;
|
||||
private TextBox LocationBox;
|
||||
}
|
||||
}
|
||||
157
OpenSaveCloudClient/DownloadGameForm.cs
Normal file
157
OpenSaveCloudClient/DownloadGameForm.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using OpenSaveCloudClient.Core;
|
||||
using OpenSaveCloudClient.Models;
|
||||
using OpenSaveCloudClient.Models.Remote;
|
||||
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 DownloadGameForm : Form
|
||||
{
|
||||
|
||||
private ServerConnector serverConnector;
|
||||
private SaveManager saveManager;
|
||||
private ListViewItem? selectedItem;
|
||||
|
||||
private List<GameSave> result;
|
||||
|
||||
public List<GameSave> Result { get { return result; } }
|
||||
|
||||
public DownloadGameForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
result = new List<GameSave>();
|
||||
serverConnector = ServerConnector.GetInstance();
|
||||
saveManager = SaveManager.GetInstance();
|
||||
}
|
||||
|
||||
private void DownloadGameForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
new Thread(() => {
|
||||
List<Game>? remoteGames = serverConnector.GetGamesInfo();
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
UpdateRemoteList(remoteGames);
|
||||
});
|
||||
}).Start();
|
||||
}
|
||||
|
||||
private void UpdateRemoteList(List<Game>? remoteGames)
|
||||
{
|
||||
if (remoteGames != null)
|
||||
{
|
||||
foreach (Game game in remoteGames)
|
||||
{
|
||||
ListViewItem lvi = RemoteList.Items.Add(game.Name);
|
||||
lvi.SubItems.Add(Convert.ToString(game.Id));
|
||||
lvi.SubItems.Add("");
|
||||
}
|
||||
}
|
||||
LockControls(false);
|
||||
}
|
||||
|
||||
private void LockControls(bool l)
|
||||
{
|
||||
LoadingIndicator.Visible = l;
|
||||
l = !l;
|
||||
RemoteList.Enabled = l;
|
||||
}
|
||||
|
||||
private void pathButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
FolderBrowserDialog dialog = new();
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string path = dialog.SelectedPath;
|
||||
LocationBox.Text = path;
|
||||
if (selectedItem != null && selectedItem.Checked)
|
||||
{
|
||||
selectedItem.SubItems[2].Text = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteList_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
selectedItem = null;
|
||||
if (RemoteList.SelectedItems.Count == 1)
|
||||
{
|
||||
selectedItem = RemoteList.SelectedItems[0];
|
||||
if (selectedItem != null && selectedItem.Checked)
|
||||
{
|
||||
pathButton.Enabled = true;
|
||||
LocationBox.Enabled = true;
|
||||
LocationBox.Text = selectedItem.SubItems[2].Text;
|
||||
}
|
||||
|
||||
} else
|
||||
{
|
||||
pathButton.Enabled = false;
|
||||
LocationBox.Enabled = false;
|
||||
LocationBox.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoteList_ItemChecked(object sender, ItemCheckedEventArgs e)
|
||||
{
|
||||
DownloadButton.Enabled = (RemoteList.CheckedItems.Count > 0);
|
||||
RemoteList_SelectedIndexChanged(sender, e);
|
||||
}
|
||||
|
||||
private void LocationBox_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (selectedItem != null && selectedItem.Checked)
|
||||
{
|
||||
selectedItem.SubItems[2].Text = LocationBox.Text;
|
||||
}
|
||||
}
|
||||
|
||||
private void DownloadButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
foreach (ListViewItem lvi in RemoteList.CheckedItems)
|
||||
{
|
||||
string path = lvi.SubItems[2].Text;
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
MessageBox.Show("File folder cannot be empty", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (saveManager.Saves.Exists(g => g.FolderPath == path))
|
||||
{
|
||||
MessageBox.Show("This directory is already used for another game", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
if (Directory.EnumerateFileSystemEntries(path).Any())
|
||||
{
|
||||
string msg = String.Format("The directory '{0}' contains files, these files will be deleted. Do you want to continue using this folder?", path);
|
||||
if (MessageBox.Show(
|
||||
msg,
|
||||
"Directory not empty",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question) == DialogResult.No)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
GameSave gameSave = new(lvi.SubItems[0].Text, "", path, "", 0);
|
||||
gameSave.Id = Int64.Parse(lvi.SubItems[1].Text);
|
||||
result.Add(gameSave);
|
||||
}
|
||||
DialogResult = DialogResult.OK;
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
2624
OpenSaveCloudClient/DownloadGameForm.resx
Normal file
2624
OpenSaveCloudClient/DownloadGameForm.resx
Normal file
File diff suppressed because it is too large
Load Diff
28
OpenSaveCloudClient/GameLibraryForm.Designer.cs
generated
28
OpenSaveCloudClient/GameLibraryForm.Designer.cs
generated
@@ -33,9 +33,11 @@
|
||||
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
|
||||
this.toolStripDropDownButton1 = new System.Windows.Forms.ToolStripDropDownButton();
|
||||
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
|
||||
this.toolStripProgressBar1 = new System.Windows.Forms.ToolStripProgressBar();
|
||||
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
|
||||
this.AddButton = new System.Windows.Forms.ToolStripButton();
|
||||
this.SyncButton = new System.Windows.Forms.ToolStripButton();
|
||||
this.DownloadButton = new System.Windows.Forms.ToolStripButton();
|
||||
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
|
||||
this.ConfigButton = new System.Windows.Forms.ToolStripButton();
|
||||
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
|
||||
@@ -53,7 +55,8 @@
|
||||
this.statusStrip1.ImageScalingSize = new System.Drawing.Size(24, 24);
|
||||
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.toolStripDropDownButton1,
|
||||
this.toolStripStatusLabel1});
|
||||
this.toolStripStatusLabel1,
|
||||
this.toolStripProgressBar1});
|
||||
this.statusStrip1.Location = new System.Drawing.Point(0, 803);
|
||||
this.statusStrip1.Name = "statusStrip1";
|
||||
this.statusStrip1.Size = new System.Drawing.Size(1428, 32);
|
||||
@@ -78,6 +81,14 @@
|
||||
this.toolStripStatusLabel1.Size = new System.Drawing.Size(60, 25);
|
||||
this.toolStripStatusLabel1.Text = "Ready";
|
||||
//
|
||||
// toolStripProgressBar1
|
||||
//
|
||||
this.toolStripProgressBar1.MarqueeAnimationSpeed = 20;
|
||||
this.toolStripProgressBar1.Name = "toolStripProgressBar1";
|
||||
this.toolStripProgressBar1.Size = new System.Drawing.Size(100, 24);
|
||||
this.toolStripProgressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
|
||||
this.toolStripProgressBar1.Visible = false;
|
||||
//
|
||||
// toolStrip1
|
||||
//
|
||||
this.toolStrip1.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
|
||||
@@ -85,6 +96,7 @@
|
||||
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.AddButton,
|
||||
this.SyncButton,
|
||||
this.DownloadButton,
|
||||
this.toolStripSeparator1,
|
||||
this.ConfigButton,
|
||||
this.toolStripSeparator2,
|
||||
@@ -119,6 +131,17 @@
|
||||
this.SyncButton.Text = "Synchronize";
|
||||
this.SyncButton.Click += new System.EventHandler(this.SyncButton_Click);
|
||||
//
|
||||
// DownloadButton
|
||||
//
|
||||
this.DownloadButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
|
||||
this.DownloadButton.Enabled = false;
|
||||
this.DownloadButton.Image = ((System.Drawing.Image)(resources.GetObject("DownloadButton.Image")));
|
||||
this.DownloadButton.ImageTransparentColor = System.Drawing.Color.Magenta;
|
||||
this.DownloadButton.Name = "DownloadButton";
|
||||
this.DownloadButton.Size = new System.Drawing.Size(34, 28);
|
||||
this.DownloadButton.Text = "Download";
|
||||
this.DownloadButton.Click += new System.EventHandler(this.DownloadButton_Click);
|
||||
//
|
||||
// toolStripSeparator1
|
||||
//
|
||||
this.toolStripSeparator1.Name = "toolStripSeparator1";
|
||||
@@ -186,6 +209,7 @@
|
||||
this.listView1.Size = new System.Drawing.Size(1428, 764);
|
||||
this.listView1.TabIndex = 2;
|
||||
this.listView1.UseCompatibleStateImageBehavior = false;
|
||||
this.listView1.DoubleClick += new System.EventHandler(this.listView1_DoubleClick);
|
||||
//
|
||||
// coverList
|
||||
//
|
||||
@@ -235,5 +259,7 @@
|
||||
private ToolStripButton AboutButton;
|
||||
private ToolStripButton ErrorLogButton;
|
||||
private ToolStripDropDownButton toolStripDropDownButton1;
|
||||
private ToolStripButton DownloadButton;
|
||||
private ToolStripProgressBar toolStripProgressBar1;
|
||||
}
|
||||
}
|
||||
@@ -49,15 +49,13 @@ namespace OpenSaveCloudClient
|
||||
else
|
||||
{
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
LogoutButton.Enabled = true;
|
||||
AboutButton.Enabled = true;
|
||||
if (_configuration.GetBoolean("synchronization.at_login", true))
|
||||
{
|
||||
SyncButton_Click(sender, e);
|
||||
} else
|
||||
{
|
||||
AddButton.Enabled = true;
|
||||
SyncButton.Enabled = true;
|
||||
LockCriticalControls(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -81,7 +79,6 @@ namespace OpenSaveCloudClient
|
||||
} else
|
||||
{
|
||||
Enabled = true;
|
||||
LogoutButton.Enabled = true;
|
||||
AboutButton.Enabled = true;
|
||||
if (_configuration.GetBoolean("synchronization.at_login", true))
|
||||
{
|
||||
@@ -89,8 +86,7 @@ namespace OpenSaveCloudClient
|
||||
}
|
||||
else
|
||||
{
|
||||
AddButton.Enabled = true;
|
||||
SyncButton.Enabled = true;
|
||||
LockCriticalControls(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,6 +146,7 @@ namespace OpenSaveCloudClient
|
||||
foreach (GameSave game in saveManager.Saves)
|
||||
{
|
||||
ListViewItem itm = listView1.Items.Add(game.Name);
|
||||
itm.SubItems.Add(game.Uuid);
|
||||
itm.ImageKey = "unknown_cover.png";
|
||||
}
|
||||
}
|
||||
@@ -201,25 +198,16 @@ namespace OpenSaveCloudClient
|
||||
text = String.Format("Ended: {0}", e.TaskInformation.Label);
|
||||
break;
|
||||
}
|
||||
if (taskManager.TasksInformation.Count > 1)
|
||||
{
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
toolStripStatusLabel1.Text = String.Format("{0} (and {1} more)", text, taskManager.TasksInformation.Count);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
toolStripStatusLabel1.Text = text;
|
||||
});
|
||||
}
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
toolStripProgressBar1.Visible = (taskManager.TasksInformation.Count(ti => ti.Status == AsyncTaskStatus.Running) > 0);
|
||||
toolStripStatusLabel1.Text = text;
|
||||
});
|
||||
}
|
||||
|
||||
private void LogoutButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
serverConnector.Logout();
|
||||
AddButton.Enabled = false;
|
||||
LogoutButton.Enabled = false;
|
||||
LockCriticalControls(true);
|
||||
AboutButton.Enabled = false;
|
||||
ShowLoginForm();
|
||||
}
|
||||
@@ -278,15 +266,112 @@ namespace OpenSaveCloudClient
|
||||
|
||||
private void SyncButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
AddButton.Enabled = false;
|
||||
SyncButton.Enabled = false;
|
||||
LockCriticalControls(true);
|
||||
new Thread(() => {
|
||||
serverConnector.Synchronize();
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
AddButton.Enabled = true;
|
||||
SyncButton.Enabled = true;
|
||||
LockCriticalControls(false);
|
||||
});
|
||||
}).Start();
|
||||
}
|
||||
|
||||
private void DownloadButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
DownloadGameForm form = new DownloadGameForm();
|
||||
if (form.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
List<GameSave> newGames = form.Result;
|
||||
new Thread(async () =>
|
||||
{
|
||||
string taskUuid = StartTask("Downloading games from server", false, newGames.Count);
|
||||
foreach (GameSave gameSave in newGames)
|
||||
{
|
||||
try
|
||||
{
|
||||
saveManager.Saves.Add(gameSave);
|
||||
List<GameSave> l = new()
|
||||
{
|
||||
gameSave
|
||||
};
|
||||
await serverConnector.DownloadGamesAsync(l);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
logManager.AddError(ex);
|
||||
}
|
||||
taskManager.UpdateTaskProgress(taskUuid, 1);
|
||||
}
|
||||
saveManager.Save();
|
||||
SetTaskEnded(taskUuid);
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
RefreshList();
|
||||
});
|
||||
}).Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void LockCriticalControls(bool l)
|
||||
{
|
||||
l = !l;
|
||||
AddButton.Enabled = l;
|
||||
SyncButton.Enabled = l;
|
||||
DownloadButton.Enabled = l;
|
||||
LogoutButton.Enabled = l;
|
||||
}
|
||||
|
||||
private void listView1_DoubleClick(object sender, EventArgs e)
|
||||
{
|
||||
if (listView1.SelectedItems.Count == 1 && SyncButton.Enabled)
|
||||
{
|
||||
GameSave? g = saveManager.Saves.FirstOrDefault(g => g.Uuid == listView1.SelectedItems[0].SubItems[1].Text);
|
||||
if (g != null)
|
||||
{
|
||||
DetailForm detail = new(g);
|
||||
if (detail.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
ForcedSyncResult? r = detail.Result;
|
||||
if (r != null)
|
||||
{
|
||||
LockCriticalControls(true);
|
||||
new Thread(async () =>
|
||||
{
|
||||
List<GameSave> l = new()
|
||||
{
|
||||
g
|
||||
};
|
||||
string taskUuid;
|
||||
switch (r)
|
||||
{
|
||||
case ForcedSyncResult.Download:
|
||||
taskUuid = StartTask("Forcing download of " + g.Name, true, 1);
|
||||
try
|
||||
{
|
||||
await serverConnector.DownloadGamesAsync(l);
|
||||
} finally
|
||||
{
|
||||
SetTaskEnded(taskUuid);
|
||||
}
|
||||
break;
|
||||
case ForcedSyncResult.Upload:
|
||||
taskUuid = StartTask("Forcing upload of " + g.Name, true, 1);
|
||||
try
|
||||
{
|
||||
g.Archive();
|
||||
serverConnector.UploadGames(l);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SetTaskEnded(taskUuid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.Invoke((MethodInvoker)delegate {
|
||||
LockCriticalControls(false);
|
||||
});
|
||||
}).Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,18 @@
|
||||
8NaJQncgBYfJB0E8K2YLaCUfuj70UJc7r5ECaIYt2oqdXnrfUufBqlcAifVey7AbvdcyafSS6BWgHK3q
|
||||
6J3SNDwd2W4vlQSaKYD1ll1RlMX1dE0BdL062TKyC2DO6Tg2uazrFoKS1KiO/PtXkt5sNpvNAKfTD+zV
|
||||
uLXp5ptQAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="DownloadButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
|
||||
YQUAAAAJcEhZcwAAFiUAABYlAUlSJPAAAAFPSURBVGhD7ZlhbQMxDIUPwiAMwiAMwiAMwhgMwiAMSiEU
|
||||
QiEUwuavmiXr5HSXnGpfJH/S+5Mq8Xu9NnbVpSiKh/L8pyn5El1FP6JPFmbiSaTmVVM9iVeRNY9Ym4YK
|
||||
kE0FyKYCZFMBspkmACODx9YArf0hfIh03mFws/wXAON22OOsUDBwEVmDJ5FyLwB7v0X2NYKEgglrQKUh
|
||||
WgE884gA4dOqZwQRwgvwJmrtYT0FzHqG1h8vdHbWEGfwZNJohdiidPPKSIjDmFd6QhzOvLIlxGHNK/dC
|
||||
hJp/EdEtUe8d7YXoNc9VPFr/hr32RjqlDTHyzturuLu+14h4Ir3QvLQD97C7vncAa1Hsrl8BdlIBKsBO
|
||||
HhKAhvIeJP5PWNfvCsC9vT4gW93duPUDJEN08m5I7B0WLcaIkSngBhu9wSxK1B42v4YvUaTCxu6iKNJY
|
||||
ll87VrNiCYfhAAAAAABJRU5ErkJggg==
|
||||
</value>
|
||||
</data>
|
||||
<data name="ConfigButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
@@ -167,7 +179,7 @@
|
||||
AAEAAAD/////AQAAAAAAAAAMAgAAAEZTeXN0ZW0uV2luZG93cy5Gb3JtcywgQ3VsdHVyZT1uZXV0cmFs
|
||||
LCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAAAmU3lzdGVtLldpbmRvd3MuRm9ybXMu
|
||||
SW1hZ2VMaXN0U3RyZWFtZXIBAAAABERhdGEHAgIAAAAJAwAAAA8DAAAA7OMCAAJNU0Z0AUkBTAMBAQAB
|
||||
iAEAAYgBAAEIAQEBdgEBBP8BGQEACP8BQgFNATYHAAE2AwABKAMAASABBAIAAXYBAQIAAQEBAAEYBQAB
|
||||
+AEAAfgBAAEIAQEBdgEBBP8BGQEACP8BQgFNATYHAAE2AwABKAMAASABBAIAAXYBAQIAAQEBAAEYBQAB
|
||||
QAEUARIRAANSA1EBUgFRBVICUQNSAlEDUgJRAVIEUQFSBVEBUg1RAVAIUQFQAlEBUAFRAVADUQFQAVEE
|
||||
UAFRAVABUQJQAVECUAFRAVABUQ5QAU8BUAFPAlADTwZQAU8BUAFPAVADTwNQAU8CUAZPAVABTwFQBU8B
|
||||
UAZPAU4ETwFOBE8BTgVPAU4DTwJOBE8CTgJPAU4BTwFOAU8HTgFPCE4BTwdOAU0GTgJNBE4BTQNOAk0B
|
||||
|
||||
@@ -10,14 +10,14 @@ namespace OpenSaveCloudClient.Models
|
||||
{
|
||||
|
||||
private readonly string label;
|
||||
private readonly int max;
|
||||
private int max;
|
||||
private int progress;
|
||||
private AsyncTaskStatus status;
|
||||
private readonly string uuid;
|
||||
private bool undefined;
|
||||
|
||||
public string Label { get { return label; } }
|
||||
public int Max { get { return max; } }
|
||||
public int Max { get { return max; } set { max = value; } }
|
||||
public int Progress { get { return progress; } set { progress = value; } }
|
||||
public AsyncTaskStatus Status { get { return status; } set { status = value; } }
|
||||
|
||||
|
||||
@@ -6,33 +6,34 @@ using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using System.IO.Compression;
|
||||
using System.Security.Cryptography;
|
||||
using OpenSaveCloudClient.Core;
|
||||
|
||||
namespace OpenSaveCloudClient.Models
|
||||
{
|
||||
public class GameSave
|
||||
{
|
||||
|
||||
private int id;
|
||||
private long id;
|
||||
private string uuid;
|
||||
private readonly string name;
|
||||
private readonly string folderPath;
|
||||
private readonly string description;
|
||||
private string hash;
|
||||
private string currentHash;
|
||||
private readonly string? coverPath;
|
||||
private int revision;
|
||||
private long revision;
|
||||
private bool synced;
|
||||
private bool localOnly;
|
||||
|
||||
public int Id { get { return id; } set { id = value; } }
|
||||
public long Id { get { return id; } set { id = value; } }
|
||||
public string Uuid { get { return uuid; } }
|
||||
public string Name { get { return name; } }
|
||||
public string Description { get { return description; } }
|
||||
public string FolderPath { get { return folderPath; } }
|
||||
public string Hash { get { return hash; } }
|
||||
public string CurrentHash { get { return currentHash; } }
|
||||
public string? CoverPath { get { return coverPath; } }
|
||||
public int Revision { get { return revision; } set { revision = value; } }
|
||||
public long Revision { get { return revision; } set { revision = value; } }
|
||||
public bool Synced { get { return synced; } set { synced = value; } }
|
||||
public bool LocalOnly { get { return localOnly; } set { localOnly = value; } }
|
||||
|
||||
public GameSave(string name, string description, string folderPath, string? coverPath, int revision)
|
||||
{
|
||||
@@ -45,11 +46,10 @@ namespace OpenSaveCloudClient.Models
|
||||
this.coverPath = coverPath;
|
||||
this.revision = revision;
|
||||
synced = false;
|
||||
localOnly = true;
|
||||
}
|
||||
|
||||
[JsonConstructor]
|
||||
public GameSave(int id, string uuid, string name, string folderPath, string description, string hash, string? coverPath, int revision, bool synced, bool localOnly)
|
||||
public GameSave(long id, string uuid, string name, string folderPath, string description, string hash, string currentHash, string? coverPath, long revision, bool synced)
|
||||
{
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
@@ -57,10 +57,10 @@ namespace OpenSaveCloudClient.Models
|
||||
this.folderPath = folderPath;
|
||||
this.description = description;
|
||||
this.hash = hash;
|
||||
this.currentHash = currentHash;
|
||||
this.coverPath = coverPath;
|
||||
this.revision = revision;
|
||||
this.synced = synced;
|
||||
this.localOnly = localOnly;
|
||||
}
|
||||
|
||||
public void Archive()
|
||||
@@ -90,5 +90,25 @@ namespace OpenSaveCloudClient.Models
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool DetectChanges()
|
||||
{
|
||||
byte[]? hashBytes = HashTool.HashDirectory(FolderPath);
|
||||
if (hashBytes == null)
|
||||
{
|
||||
throw new Exception(String.Format("failed to get hash of directory '{0}'", FolderPath));
|
||||
}
|
||||
currentHash = BitConverter.ToString(hashBytes).Replace("-", "");
|
||||
if (currentHash != hash)
|
||||
{
|
||||
synced = false;
|
||||
}
|
||||
return (currentHash != hash);
|
||||
}
|
||||
|
||||
public void UpdateHash()
|
||||
{
|
||||
hash = currentHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ namespace OpenSaveCloudClient.Models.Remote
|
||||
{
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
public int Id { get; set; }
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("rev")]
|
||||
public int Revision { get; set; }
|
||||
public long Revision { get; set; }
|
||||
|
||||
[JsonPropertyName("hash")]
|
||||
public string Hash { get; set; }
|
||||
|
||||
@@ -10,6 +10,6 @@ namespace OpenSaveCloudClient.Models.Remote
|
||||
public class UploadGameInfo
|
||||
{
|
||||
[JsonPropertyName("game_id")]
|
||||
public int GameId { get; set; }
|
||||
public long GameId { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<ApplicationIcon>logo.ico</ApplicationIcon>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace OpenSaveCloudClient
|
||||
pb.Minimum = 0;
|
||||
pb.Maximum = max;
|
||||
pb.Value = value;
|
||||
pb.MarqueeAnimationSpeed = 20;
|
||||
if (undefined)
|
||||
{
|
||||
pb.Style = ProgressBarStyle.Marquee;
|
||||
|
||||
Reference in New Issue
Block a user