/// <summary> /// Verifies the integrity of the file in the manifest entry. /// </summary> /// <param name="entry">The manifest entry to test.</param> /// <returns><c>true</c>, if file was complete and undamaged, <c>false</c> otherwise.</returns> public static bool IsFileIntegrityIntact(this ManifestEntry entry) { var localPath = $"{DirectoryHelpers.GetLocalGameDirectory()}{entry.RelativePath}"; if (!File.Exists(localPath)) { return(false); } var fileInfo = new FileInfo(localPath); if (fileInfo.Length != entry.Size) { return(false); } using (Stream file = File.OpenRead(localPath)) { string localHash = MD5Handler.GetStreamHash(file); if (localHash != entry.Hash) { return(false); } } return(true); }
/// <summary> /// Loads the old manifest from disk. /// </summary> private void LoadOldManifest() { try { lock (OldManifestLock) { if (File.Exists(ConfigHandler.GetOldManifestPath())) { string[] rawOldManifest = File.ReadAllLines(ConfigHandler.GetOldManifestPath()); foreach (string rawEntry in rawOldManifest) { ManifestEntry newEntry = new ManifestEntry(); if (ManifestEntry.TryParse(rawEntry, out newEntry)) { oldManifest.Add(newEntry); } } } } } catch (IOException ioex) { Console.WriteLine("IOException in LoadOldManifest(): " + ioex.Message); } }
/// <summary> /// Attempts to parse an entry from a raw input. /// The input is expected to be in [path]:[hash]:[size] format. /// </summary> /// <returns><c>true</c>, if the input was successfully parse, <c>false</c> otherwise.</returns> /// <param name="rawInput">Raw input.</param> /// <param name="entry">The resulting entry.</param> public static bool TryParse(string rawInput, out ManifestEntry inEntry) { //clear out the entry for the new data inEntry = new ManifestEntry(); if (!String.IsNullOrEmpty(rawInput)) { //remove any and all bad characters from the input string, //such as \0, \n and \r. string cleanInput = Utilities.Clean(rawInput); //split the string into its three components - file, hash and size string[] entryElements = cleanInput.Split(':'); //if we have three elements (which we should always have), set them in the provided entry if (entryElements.Length == 3) { //clean the manifest path, converting \ to / on unix and / to \ on Windows. if (ChecksHandler.IsRunningOnUnix()) { inEntry.RelativePath = entryElements [0].Replace("\\", "/"); } else { inEntry.RelativePath = entryElements [0].Replace("/", "\\"); } //set the hash to the second element inEntry.Hash = entryElements [1]; //attempt to parse the final element as a long-type byte count. long parsedSize = 0; if (long.TryParse(entryElements[2], out parsedSize)) { inEntry.Size = parsedSize; return(true); } else { //could not parse the size, parsing has failed. return(false); } } else { //wrong number of raw entry elements, parsing has failed. return(false); } } else { //no input, parsing has failed return(false); } }
/// <summary> /// Attempts to parse an entry from a raw input. /// The input is expected to be in [path]:[hash]:[size] format. /// </summary> /// <returns><c>true</c>, if the input was successfully parse, <c>false</c> otherwise.</returns> /// <param name="rawInput">Raw input.</param> /// <param name="entry">The resulting entry.</param> public static bool TryParse(string rawInput, out ManifestEntry inEntry) { //clear out the entry for the new data inEntry = new ManifestEntry (); if (!String.IsNullOrEmpty(rawInput)) { //remove any and all bad characters from the input string, //such as \0, \n and \r. string cleanInput = Utilities.Clean (rawInput); //split the string into its three components - file, hash and size string[] entryElements = cleanInput.Split (':'); //if we have three elements (which we should always have), set them in the provided entry if (entryElements.Length == 3) { //clean the manifest path, converting \ to / on unix and / to \ on Windows. if (ChecksHandler.IsRunningOnUnix()) { inEntry.RelativePath = entryElements [0].Replace ("\\", "/"); } else { inEntry.RelativePath = entryElements [0].Replace("/", "\\"); } //set the hash to the second element inEntry.Hash = entryElements [1]; //attempt to parse the final element as a long-type byte count. long parsedSize = 0; if(long.TryParse(entryElements[2], out parsedSize)) { inEntry.Size = parsedSize; return true; } else { //could not parse the size, parsing has failed. return false; } } else { //wrong number of raw entry elements, parsing has failed. return false; } } else { //no input, parsing has failed return false; } }
private void InstallGameAsync() { //This value is filled with either a path to the last downloaded file, or with an exception message //this message is used in the main UI to determine how it responds to a failed download. string fileReturn = ""; try { FTPHandler FTP = new FTPHandler(); ManifestHandler manifestHandler = new ManifestHandler(); List <ManifestEntry> Manifest = manifestHandler.Manifest; //create the .install file to mark that an installation has begun //if it exists, do nothing. ConfigHandler.CreateInstallCookie(); //raise the progress changed event by binding to the //event in the FTP class FTP.FileProgressChanged += OnDownloadProgressChanged; //in order to be able to resume downloading, we check if there is an entry //stored in the install cookie. ManifestEntry lastDownloadedFile = null; string installCookiePath = ConfigHandler.GetInstallCookiePath(); //attempt to parse whatever is inside the install cookie if (ManifestEntry.TryParse(File.ReadAllText(installCookiePath), out lastDownloadedFile)) { //loop through all the entries in the manifest until we encounter //an entry which matches the one in the install cookie foreach (ManifestEntry Entry in Manifest) { if (lastDownloadedFile == Entry) { //remove all entries before the one we were last at. Manifest.RemoveRange(0, Manifest.IndexOf(Entry)); } } } //then, start downloading the entries that remain in the manifest. foreach (ManifestEntry Entry in Manifest) { string RemotePath = String.Format("{0}{1}", Config.GetGameURL(true), Entry.RelativePath); string LocalPath = String.Format("{0}{1}{2}", Config.GetGamePath(true), System.IO.Path.DirectorySeparatorChar, Entry.RelativePath); //make sure we have a game directory to put files in Directory.CreateDirectory(Path.GetDirectoryName(LocalPath)); //write the current file progress to the install cookie TextWriter textWriterProgress = new StreamWriter(ConfigHandler.GetInstallCookiePath()); textWriterProgress.WriteLine(Entry.ToString()); textWriterProgress.Close(); if (File.Exists(LocalPath)) { FileInfo fileInfo = new FileInfo(LocalPath); if (fileInfo.Length != Entry.Size) { //Resume the download of this partial file. OnProgressChanged(); fileReturn = FTP.DownloadFTPFile(RemotePath, LocalPath, fileInfo.Length, false); //Now verify the file string localHash = MD5Handler.GetFileHash(File.OpenRead(LocalPath)); if (localHash != Entry.Hash) { Console.WriteLine("InstallGameAsync: Resumed file hash was invalid, downloading fresh copy from server."); OnProgressChanged(); fileReturn = FTP.DownloadFTPFile(RemotePath, LocalPath, false); } } } else { //no file, download it OnProgressChanged(); fileReturn = FTP.DownloadFTPFile(RemotePath, LocalPath, false); } if (ChecksHandler.IsRunningOnUnix()) { //if we're dealing with a file that should be executable, string gameName = Config.GetGameName(); bool bFileIsGameExecutable = (Path.GetFileName(LocalPath).EndsWith(".exe")) || (Path.GetFileNameWithoutExtension(LocalPath) == gameName); if (bFileIsGameExecutable) { //set the execute bits UnixHandler.MakeExecutable(LocalPath); } } } //we've finished the download, so empty the cookie File.WriteAllText(ConfigHandler.GetInstallCookiePath(), String.Empty); //raise the finished event OnGameDownloadFinished(); //clear out the event handler FTP.FileProgressChanged -= OnDownloadProgressChanged; } catch (IOException ioex) { Console.WriteLine("IOException in InstallGameAsync(): " + ioex.Message); DownloadFailedArgs.Result = "1"; DownloadFailedArgs.ResultType = "Install"; DownloadFinishedArgs.Metadata = fileReturn; OnGameDownloadFailed(); } }
/// <summary> /// Loads the old manifest from disk. /// </summary> private void LoadOldManifest() { try { lock (OldManifestLock) { if (File.Exists(ConfigHandler.GetOldManifestPath())) { string[] rawOldManifest = File.ReadAllLines (ConfigHandler.GetOldManifestPath ()); foreach (string rawEntry in rawOldManifest) { ManifestEntry newEntry = new ManifestEntry (); if (ManifestEntry.TryParse (rawEntry, out newEntry)) { oldManifest.Add (newEntry); } } } } } catch (IOException ioex) { Console.WriteLine ("IOException in LoadOldManifest(): " + ioex.Message); } }