/// <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> /// Gets the path to the game executable. /// </summary> /// <returns>The game executable.</returns> public string GetGameExecutable() { string executablePathRootLevel = String.Empty; string executablePathTargetLevel = String.Empty; //unix doesn't need (or have!) the .exe extension. if (ChecksHandler.IsRunningOnUnix()) { //should return something along the lines of "./Game/<ExecutableName>" executablePathRootLevel = String.Format(@"{0}{1}", GetGamePath(true), GetGameName()); //should return something along the lines of "./Game/<GameName>/Binaries/<SystemTarget>/<ExecutableName>" executablePathTargetLevel = String.Format(@"{0}{1}{3}Binaries{3}{2}{3}{1}", GetGamePath(true), GetGameName(), GetSystemTarget(), Path.DirectorySeparatorChar); } else { //should return something along the lines of "./Game/<ExecutableName>.exe" executablePathRootLevel = String.Format(@"{0}{1}.exe", GetGamePath(true), GetGameName()); //should return something along the lines of "./Game/<GameName>/Binaries/<SystemTarget>/<ExecutableName>.exe" executablePathTargetLevel = String.Format(@"{0}{1}{3}Binaries{3}{2}{3}{1}.exe", GetGamePath(true), GetGameName(), GetSystemTarget(), Path.DirectorySeparatorChar); } if (File.Exists(executablePathRootLevel)) { return(executablePathRootLevel); } else if (File.Exists(executablePathTargetLevel)) { return(executablePathTargetLevel); } else { Console.WriteLine("Searched at: " + executablePathRootLevel); Console.WriteLine("Searched at: " + executablePathTargetLevel); throw new FileNotFoundException("The game executable could not be found."); } }
static void Main() { if (ChecksHandler.IsRunningOnUnix()) { // run a GTK UI instead of WinForms Gtk.Application.Init(); MainWindow win = new MainWindow(); win.Show(); Gtk.Application.Run(); } else { // run a WinForms UI instead of GTK System.Windows.Forms.Application.EnableVisualStyles(); System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); System.Windows.Forms.Application.Run(new MainForm()); } }
static void Main() { // Set correct working directory for compatibility with double-clicking Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); if (ChecksHandler.IsRunningOnUnix()) { // run a GTK UI instead of WinForms Gtk.Application.Init(); MainWindow win = new MainWindow(); win.Show(); Gtk.Application.Run(); } else { // run a WinForms UI instead of GTK System.Windows.Forms.Application.EnableVisualStyles(); System.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false); System.Windows.Forms.Application.Run(new MainForm()); } }
private static void Main() { // Bind any unhandled exceptions in the main thread so that they are logged. AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; Log.Info("----------------"); Log.Info($"Launchpad v{Config.GetLocalLauncherVersion()} starting..."); Log.Info($"Current platform: {ConfigHandler.GetCurrentPlatform()} ({(Environment.Is64BitOperatingSystem ? "x64" : "x86")})"); // Set correct working directory for compatibility with double-clicking Directory.SetCurrentDirectory(ConfigHandler.GetLocalDir()); if (ChecksHandler.IsRunningOnUnix()) { Log.Info("Initializing GTK UI."); RunUnixInterface(); } else { Log.Info("Initializing WinForms UI."); RunWindowsInterface(); } }
/// <summary> /// Creates the update script on disk. /// </summary> /// <returns>ProcessStartInfo for the update script.</returns> private static ProcessStartInfo CreateUpdateScript() { try { //maintain the executable name if it was renamed to something other than 'Launchpad' string fullName = Assembly.GetEntryAssembly().Location; string executableName = Path.GetFileName(fullName); // should be "Launchpad", unless the user has renamed it if (ChecksHandler.IsRunningOnUnix()) { //creating a .sh script string scriptPath = String.Format(@"{0}launchpadupdate.sh", ConfigHandler.GetTempDir()); FileStream updateScript = File.Create(scriptPath); TextWriter tw = new StreamWriter(updateScript); //write commands to the script //wait five seconds, then copy the new executable string copyCom = String.Format("cp -rf {0} {1}", ConfigHandler.GetTempDir() + "launchpad/*", ConfigHandler.GetLocalDir()); string delCom = String.Format("rm -rf {0}", ConfigHandler.GetTempDir() + "launchpad"); string dirCom = String.Format("cd {0}", ConfigHandler.GetLocalDir()); string launchCom = String.Format(@"nohup ./{0} &", executableName); tw.WriteLine(@"#!/bin/sh"); tw.WriteLine("sleep 5"); tw.WriteLine(copyCom); tw.WriteLine(delCom); tw.WriteLine(dirCom); tw.WriteLine("chmod +x " + executableName); tw.WriteLine(launchCom); tw.Close(); UnixHandler.MakeExecutable(scriptPath); //Now create some ProcessStartInfo for this script ProcessStartInfo updateShellProcess = new ProcessStartInfo(); updateShellProcess.FileName = scriptPath; updateShellProcess.UseShellExecute = false; updateShellProcess.RedirectStandardOutput = false; updateShellProcess.WindowStyle = ProcessWindowStyle.Hidden; return(updateShellProcess); } else { //creating a .bat script string scriptPath = String.Format(@"{0}launchpadupdate.bat", ConfigHandler.GetTempDir()); FileStream updateScript = File.Create(scriptPath); TextWriter tw = new StreamWriter(updateScript); //write commands to the script //wait three seconds, then copy the new executable tw.WriteLine(String.Format(@"timeout 3 & xcopy /e /s /y ""{0}\launchpad"" ""{1}"" && rmdir /s /q {0}\launchpad", ConfigHandler.GetTempDir(), ConfigHandler.GetLocalDir())); //then start the new executable tw.WriteLine(String.Format(@"start {0}", executableName)); tw.Close(); ProcessStartInfo updateBatchProcess = new ProcessStartInfo(); updateBatchProcess.FileName = scriptPath; updateBatchProcess.UseShellExecute = true; updateBatchProcess.RedirectStandardOutput = false; updateBatchProcess.WindowStyle = ProcessWindowStyle.Hidden; return(updateBatchProcess); } } catch (IOException ioex) { Console.WriteLine("IOException in CreateUpdateScript(): " + ioex.Message); return(null); } }
private void RepairGameAsync() { //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 repairMetadata = ""; try { //check all local file MD5s against latest manifest. Resume partial files, download broken files. FTPHandler FTP = new FTPHandler(); //bind event handlers FTP.FileProgressChanged += OnDownloadProgressChanged; //first, verify that the manifest is correct. string LocalManifestHash = MD5Handler.GetFileHash(File.OpenRead(ConfigHandler.GetManifestPath())); string RemoteManifestHash = FTP.GetRemoteManifestChecksum(); //if it is not, download a new copy. if (!(LocalManifestHash == RemoteManifestHash)) { LauncherHandler Launcher = new LauncherHandler(); Launcher.DownloadManifest(); } //then, begin repairing the game ManifestHandler manifestHandler = new ManifestHandler(); List <ManifestEntry> Manifest = manifestHandler.Manifest; ProgressArgs.TotalFiles = Manifest.Count; int i = 0; foreach (ManifestEntry Entry in Manifest) { string RemotePath = String.Format("{0}{1}", Config.GetGameURL(true), Entry.RelativePath); string LocalPath = String.Format("{0}{1}", Config.GetGamePath(true), Entry.RelativePath); ProgressArgs.FileName = Path.GetFileName(LocalPath); //make sure the directory for the file exists Directory.CreateDirectory(Directory.GetParent(LocalPath).ToString()); if (File.Exists(LocalPath)) { FileInfo fileInfo = new FileInfo(LocalPath); if (fileInfo.Length != Entry.Size) { //Resume the download of this partial file. OnProgressChanged(); repairMetadata = FTP.DownloadFTPFile(RemotePath, LocalPath, fileInfo.Length, false); //Now verify the file string localHash = MD5Handler.GetFileHash(File.OpenRead(LocalPath)); if (localHash != Entry.Hash) { Console.WriteLine("RepairGameAsync: Resumed file hash was invalid, downloading fresh copy from server."); //download the file, since it was broken OnProgressChanged(); repairMetadata = FTP.DownloadFTPFile(RemotePath, LocalPath, false); } } } else { //download the file, since it was missing OnProgressChanged(); repairMetadata = 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); } } ++i; ProgressArgs.DownloadedFiles = i; OnProgressChanged(); } OnGameRepairFinished(); //clear out the event handler FTP.FileProgressChanged -= OnDownloadProgressChanged; } catch (IOException ioex) { Console.WriteLine("IOException in RepairGameAsync(): " + ioex.Message); DownloadFailedArgs.Result = "1"; DownloadFailedArgs.ResultType = "Repair"; DownloadFailedArgs.Metadata = repairMetadata; OnGameRepairFailed(); } }
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> /// Replaces and updates the old pre-unix config. /// </summary> /// <returns><c>true</c>, if an old config was copied over to the new format, <c>false</c> otherwise.</returns> private bool UpdateOldConfig() { string oldConfigPath = String.Format(@"{0}config{1}launcherConfig.ini", GetLocalDir(), Path.DirectorySeparatorChar); string oldConfigDir = String.Format(@"{0}config", GetLocalDir()); if (ChecksHandler.IsRunningOnUnix()) { //Case sensitive //Is there an old config file? if (File.Exists(oldConfigPath)) { lock (ReadLock) { //Have we not already created the new config dir? if (!Directory.Exists(GetConfigDir())) { //if not, create it. Directory.CreateDirectory(GetConfigDir()); //Copy the old config file to the new location. File.Copy(oldConfigPath, GetConfigPath()); //read our new file. FileIniDataParser Parser = new FileIniDataParser(); IniData data = Parser.ReadFile(GetConfigPath()); //replace the old invalid keys with new, updated keys. string launcherVersion = data["Local"]["launcherVersion"]; string gameName = data["Local"]["gameName"]; string systemTarget = data["Local"]["systemTarget"]; data["Local"].RemoveKey("launcherVersion"); data["Local"].RemoveKey("gameName"); data["Local"].RemoveKey("systemTarget"); data["Local"].AddKey("LauncherVersion", launcherVersion); data["Local"].AddKey("GameName", gameName); data["Local"].AddKey("SystemTarget", systemTarget); WriteConfig(Parser, data); //We were successful, so return true. File.Delete(oldConfigPath); Directory.Delete(oldConfigDir, true); return(true); } else { //The new config dir already exists, so we'll just toss out the old one. //Delete the old config File.Delete(oldConfigPath); Directory.Delete(oldConfigDir, true); return(false); } } } else { return(false); } } else { lock (ReadLock) { //Windows is not case sensitive, so we'll use direct access without copying. if (File.Exists(oldConfigPath)) { FileIniDataParser Parser = new FileIniDataParser(); IniData data = Parser.ReadFile(GetConfigPath()); //replace the old invalid keys with new, updated keys. string launcherVersion = data["Local"]["launcherVersion"]; string gameName = data["Local"]["gameName"]; string systemTarget = data["Local"]["systemTarget"]; data["Local"].RemoveKey("launcherVersion"); data["Local"].RemoveKey("gameName"); data["Local"].RemoveKey("systemTarget"); data["Local"].AddKey("LauncherVersion", launcherVersion); data["Local"].AddKey("GameName", gameName); data["Local"].AddKey("SystemTarget", systemTarget); WriteConfig(Parser, data); //We were successful, so return true. return(true); } else { return(false); } } } }