public override bool Equals(object obj) { PSO2FileChecksum target = obj as PSO2FileChecksum; if (target != null) { if (Leayal.StringHelper.IsEqual(this.RelativePath, target.RelativePath, true)) { if (Leayal.StringHelper.IsEqual(this.MD5, target.MD5, true)) { if (this.FileSize == target.FileSize) { return(true); } } } } return(false); }
public new IEnumerable <PSO2FileChecksum> ReadToEnd() { if (result != null) { return(result); } result = new List <PSO2FileChecksum>(); PSO2FileChecksum read = null; while (!this.EndOfStream) { read = this.ReadLine(); if (read != null) { result.Add(read); } } return(result); }
public void ReadChecksumCache(FileStream stream) { if (this._disposed) { throw new ObjectDisposedException("ChecksumCache"); } if (this.myCheckSumList != null) { return; } if (stream.Length > (ChecksumCacheVersion.Signature.Length + 4)) { try { if (stream.Position != ChecksumCacheVersion.Signature.Length) { stream.Seek(ChecksumCacheVersion.Signature.Length, SeekOrigin.Begin); } using (BinaryReader br = new BinaryReader(stream, Encoding.Unicode, true)) this._checksumVersion = new Version(br.ReadByte(), br.ReadByte(), br.ReadByte(), br.ReadByte()); using (ZlibStream compressStream = new ZlibStream(stream, CompressionMode.Decompress, CompressionLevel.BestCompression, true, Encoding.Unicode)) using (BinaryReader br = new BinaryReader(compressStream, Encoding.Unicode, true)) using (ChecksumCacheReader ccr = new ChecksumCacheReader(compressStream)) { this._PSO2Version = br.ReadString(); int count = br.ReadInt32(); Dictionary <string, PSO2FileChecksum> dict = new Dictionary <string, PSO2FileChecksum>(count, StringComparer.OrdinalIgnoreCase); PSO2FileChecksum tmpline = null; for (int i = 0; i < count; i++) { tmpline = ccr.ReadLine(); if (tmpline != null) { dict[tmpline.RelativePath] = tmpline; } else { this._corruptEntryCount++; } } this.myCheckSumList = new ConcurrentDictionary <string, PSO2FileChecksum>(dict, StringComparer.OrdinalIgnoreCase); } } catch (Exception) { if (this.myCheckSumList == null) { this.myCheckSumList = new ConcurrentDictionary <string, PSO2FileChecksum>(StringComparer.OrdinalIgnoreCase); } else { this.myCheckSumList.Clear(); } } } else { if (this.myCheckSumList == null) { this.myCheckSumList = new ConcurrentDictionary <string, PSO2FileChecksum>(StringComparer.OrdinalIgnoreCase); } else { this.myCheckSumList.Clear(); } } }
/// <summary> /// Verify and redownload any missing/old files /// </summary> /// <param name="clientDirectory">The pso2_dir directory</param> /// <param name="version">Specify the version</param> /// <param name="filelist">Specify the patchlist to check and download</param> /// <param name="options">Provide the options for current download sessions</param> public async Task VerifyAndDownloadAsync(string clientDirectory, ClientVersionCheckResult version, RemotePatchlist filelist, ClientUpdateOptions options) { ConcurrentDictionary <PSO2File, Exception> failedfiles = new ConcurrentDictionary <PSO2File, Exception>(); int currentprogress = 0, downloadedfiles = 0; CancellationTokenSource totalCancelSource = new CancellationTokenSource(); options.ParallelOptions.CancellationToken = totalCancelSource.Token; //* options.ParallelOptions.CancellationToken.Register(() => { if (options.ChecksumCache != null && downloadedfiles > 0) { this.StepChanged?.Invoke(UpdateStep.WriteCache, options.ChecksumCache); options.ChecksumCache.WriteChecksumCache(version.LatestVersion); } options.Dispose(); this.UpdateCompleted?.Invoke(new PSO2NotifyEventArgs(true, clientDirectory, new ReadOnlyDictionary <PSO2File, Exception>(failedfiles))); }); //*/ this.cancelBag.Add(totalCancelSource); this.StepChanged.Invoke(UpdateStep.BeginFileCheckAndDownload, null); if (options.ChecksumCache == null) { options.Profile = UpdaterProfile.PreferAccuracy; } else { options.ChecksumCache.ReadChecksumCache(); } ConcurrentBag <PSO2File> pso2fileBag = new ConcurrentBag <PSO2File>(filelist.Values); Func <Task> actionV; switch (options.Profile) { case UpdaterProfile.PreferAccuracy: actionV = new Func <Task>(async() => { PSO2File pso2file; while (!totalCancelSource.IsCancellationRequested && pso2fileBag.TryTake(out pso2file)) { string fullpath = Path.Combine(clientDirectory, pso2file.WindowFilename); try { if (string.Equals(pso2file.SafeFilename, DefaultValues.CensorFilename, StringComparison.OrdinalIgnoreCase)) { if (File.Exists(fullpath)) { string md5fromfile = MD5Wrapper.HashFromFile(fullpath); if (!string.Equals(md5fromfile, pso2file.MD5Hash, StringComparison.OrdinalIgnoreCase)) { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); if (options.ChecksumCache != null) { ChecksumCache.PSO2FileChecksum newchecksum = ChecksumCache.PSO2FileChecksum.FromFile(clientDirectory, fullpath); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); } this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } else { if (!options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, pso2file.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); Interlocked.Increment(ref downloadedfiles); } } } else { fullpath = Path.Combine(clientDirectory, pso2file.WindowFilename + ".backup"); if (File.Exists(fullpath)) { string md5fromfile = MD5Wrapper.HashFromFile(fullpath); if (!string.Equals(md5fromfile, pso2file.MD5Hash, StringComparison.OrdinalIgnoreCase)) { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); if (options.ChecksumCache != null) { ChecksumCache.PSO2FileChecksum newchecksum = ChecksumCache.PSO2FileChecksum.FromFile(clientDirectory, fullpath); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); } this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } else { if (!options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, pso2file.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); Interlocked.Increment(ref downloadedfiles); } } } } } else { if (File.Exists(fullpath)) { string md5fromfile = MD5Wrapper.HashFromFile(fullpath); if (!string.Equals(md5fromfile, pso2file.MD5Hash, StringComparison.OrdinalIgnoreCase)) { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); if (options.ChecksumCache != null) { ChecksumCache.PSO2FileChecksum newchecksum = ChecksumCache.PSO2FileChecksum.FromFile(clientDirectory, fullpath); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); } this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } else { if (!options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, pso2file.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); Interlocked.Increment(ref downloadedfiles); } } } else { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); if (options.ChecksumCache != null) { ChecksumCache.PSO2FileChecksum newchecksum = ChecksumCache.PSO2FileChecksum.FromFile(clientDirectory, fullpath); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); } this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } } } #if !DEBUG catch (Exception ex) { failedfiles.TryAdd(pso2file, ex); } #endif finally { try { File.Delete(fullpath + ".dtmp"); } catch { } Interlocked.Increment(ref currentprogress); this.ProgressChanged?.Invoke(currentprogress, filelist.Count); } } }); break; case UpdaterProfile.PreferSpeed: actionV = new Func <Task>(async() => { PSO2File pso2file; while (!totalCancelSource.IsCancellationRequested && pso2fileBag.TryTake(out pso2file)) { string fullpath = Path.Combine(clientDirectory, pso2file.WindowFilename); try { if (!string.Equals(pso2file.SafeFilename, DefaultValues.CensorFilename, StringComparison.OrdinalIgnoreCase)) { if (options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { if (!string.Equals(options.ChecksumCache.ChecksumList[pso2file.Filename].MD5, pso2file.MD5Hash, StringComparison.OrdinalIgnoreCase)) { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); ChecksumCache.PSO2FileChecksum newchecksum = ChecksumCache.PSO2FileChecksum.FromFile(clientDirectory, fullpath); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } } else { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); ChecksumCache.PSO2FileChecksum newchecksum = ChecksumCache.PSO2FileChecksum.FromFile(clientDirectory, fullpath); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } } } #if !DEBUG catch (Exception ex) { failedfiles.TryAdd(pso2file, ex); } #endif finally { try { File.Delete(fullpath + ".dtmp"); } catch { } Interlocked.Increment(ref currentprogress); this.ProgressChanged?.Invoke(currentprogress, filelist.Count); } } }); break; default: actionV = new Func <Task>(async() => { PSO2File pso2file; while (!totalCancelSource.IsCancellationRequested && pso2fileBag.TryTake(out pso2file)) { string fullpath = Path.Combine(clientDirectory, pso2file.WindowFilename); try { if (string.Equals(pso2file.SafeFilename, DefaultValues.CensorFilename, StringComparison.OrdinalIgnoreCase)) { if (File.Exists(fullpath)) { string md5fromfile = null; if (options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { var checksumfile = options.ChecksumCache.ChecksumList[pso2file.Filename]; using (FileStream fs = File.OpenRead(fullpath)) if (fs.Length == checksumfile.FileSize) { md5fromfile = checksumfile.MD5; } } if (string.IsNullOrEmpty(md5fromfile)) { md5fromfile = MD5Wrapper.HashFromFile(fullpath); } if (!string.Equals(md5fromfile, pso2file.MD5Hash, StringComparison.OrdinalIgnoreCase)) { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) { try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, fs.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } else { if (!options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, pso2file.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); Interlocked.Increment(ref downloadedfiles); } } } else { fullpath = Path.Combine(clientDirectory, pso2file.Filename + ".backup"); if (File.Exists(fullpath)) { string md5fromfile = null; if (options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { var checksumfile = options.ChecksumCache.ChecksumList[pso2file.Filename]; using (FileStream fs = File.OpenRead(fullpath)) if (fs.Length == checksumfile.FileSize) { md5fromfile = checksumfile.MD5; } } if (string.IsNullOrEmpty(md5fromfile)) { md5fromfile = MD5Wrapper.HashFromFile(fullpath); } if (!string.Equals(md5fromfile, pso2file.MD5Hash, StringComparison.OrdinalIgnoreCase)) { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) { try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, fs.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } else { if (!options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, pso2file.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); Interlocked.Increment(ref downloadedfiles); } } } } } else { if (File.Exists(fullpath)) { string md5fromfile = null; if (options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { var checksumfile = options.ChecksumCache.ChecksumList[pso2file.Filename]; using (FileStream fs = File.OpenRead(fullpath)) if (fs.Length == checksumfile.FileSize) { md5fromfile = checksumfile.MD5; } } if (string.IsNullOrEmpty(md5fromfile)) { md5fromfile = MD5Wrapper.HashFromFile(fullpath); } if (!string.Equals(md5fromfile, pso2file.MD5Hash, StringComparison.OrdinalIgnoreCase)) { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) { try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, fs.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } else { if (!options.ChecksumCache.ChecksumList.ContainsKey(pso2file.Filename)) { ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, pso2file.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); Interlocked.Increment(ref downloadedfiles); } } } else { this.StepChanged?.Invoke(UpdateStep.DownloadingFileStart, pso2file); using (FileStream fs = File.Create(fullpath + ".dtmp")) { try { await this.DownloadFileAsync(pso2file, fs, totalCancelSource); } catch (TaskCanceledException) { } ChecksumCache.PSO2FileChecksum newchecksum = new ChecksumCache.PSO2FileChecksum(pso2file.Filename, fs.Length, pso2file.MD5Hash); options.ChecksumCache.ChecksumList.AddOrUpdate(pso2file.Filename, newchecksum, new Func <string, ChecksumCache.PSO2FileChecksum, ChecksumCache.PSO2FileChecksum>((key, oldval) => { return(newchecksum); })); } File.Delete(fullpath); File.Move(fullpath + ".dtmp", fullpath); Interlocked.Increment(ref downloadedfiles); this.StepChanged?.Invoke(UpdateStep.DownloadingFileEnd, pso2file); } } } #if !DEBUG catch (Exception ex) { failedfiles.TryAdd(pso2file, ex); } #endif finally { try { File.Delete(fullpath + ".dtmp"); } catch { } Interlocked.Increment(ref currentprogress); this.ProgressChanged?.Invoke(currentprogress, filelist.Count); } } }); break; } try { Task[] tasks = new Task[options.ParallelOptions.MaxDegreeOfParallelism]; for (int i = 0; i < tasks.Length; i++) { tasks[i] = Task.Factory.StartNew(actionV, TaskCreationOptions.LongRunning).Unwrap(); } await Task.WhenAll(tasks); if (options.ChecksumCache != null && downloadedfiles > 0) { this.StepChanged?.Invoke(UpdateStep.WriteCache, options.ChecksumCache); options.ChecksumCache.WriteChecksumCache(version.LatestVersion); } options.Dispose(); if (!totalCancelSource.IsCancellationRequested) { if (failedfiles.Count < 4) { Settings.VersionString = version.LatestVersion; } this.UpdateCompleted?.Invoke(new PSO2NotifyEventArgs(version.LatestVersion, clientDirectory, new ReadOnlyDictionary <PSO2File, Exception>(failedfiles))); } else { this.UpdateCompleted?.Invoke(new PSO2NotifyEventArgs(true, clientDirectory, new ReadOnlyDictionary <PSO2File, Exception>(failedfiles))); } } catch (OperationCanceledException) { } catch (Exception) { if (options.ChecksumCache != null && downloadedfiles > 0) { this.StepChanged?.Invoke(UpdateStep.WriteCache, options.ChecksumCache); options.ChecksumCache.WriteChecksumCache(version.LatestVersion); } options.Dispose(); throw; } }