Compare commits

..

No commits in common. "master" and "v1.2.0.0" have entirely different histories.

17 changed files with 373 additions and 948 deletions

2
.gitignore vendored
View file

@ -1,3 +1 @@
.vs/
packages/

BIN
Newtonsoft.Json.dll Normal file

Binary file not shown.

View file

@ -1,46 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32901.82
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSLauncher", "PSLauncher\PSLauncher.csproj", "{0D6A691E-95FE-4056-8979-190A9A35BDC7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
LiveSupport|Any CPU = LiveSupport|Any CPU
LiveSupport|x64 = LiveSupport|x64
LiveSupport|x86 = LiveSupport|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|x64.ActiveCfg = Debug|x64
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|x64.Build.0 = Debug|x64
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|x86.ActiveCfg = Debug|x86
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|x86.Build.0 = Debug|x86
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|Any CPU.ActiveCfg = LiveSupport|Any CPU
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|Any CPU.Build.0 = LiveSupport|Any CPU
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|x64.ActiveCfg = LiveSupport|x64
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|x64.Build.0 = LiveSupport|x64
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|x86.ActiveCfg = LiveSupport|x86
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|x86.Build.0 = LiveSupport|x86
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|Any CPU.Build.0 = Release|Any CPU
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|x64.ActiveCfg = Release|x64
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|x64.Build.0 = Release|x64
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|x86.ActiveCfg = Release|x86
{0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {92FA1014-24E9-4E6D-A263-B80A2AD72F3C}
EndGlobalSection
EndGlobal

View file

@ -31,16 +31,16 @@ namespace PSLauncher
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fvi.FileVersion;
string contents = "# FILE AUTOGENERATED BY PSForever Launcher " + version + Environment.NewLine;
contents += "[network]" + Environment.NewLine;
string contents = "# FILE AUTOGENERATED BY PSForever Launcher " + version + "\n";
contents += "[network]\n";
for(int i = 0; i < entries.Count; i++)
{
ServerEntry entry = entries[i];
contents += "# Name: " + entry.name + Environment.NewLine;
contents += "# Name: " + entry.name + "\n";
// we only want login0 to be used, but have the rest written there for manual editing as well
contents += String.Format("{3}login{0}={1}:{2}" + Environment.NewLine, i, entry.hostname, entry.port,
contents += String.Format("{3}login{0}={1}:{2}\n", i, entry.hostname, entry.port,
i == 0 ? "" : "#");
}

View file

@ -187,7 +187,7 @@
this.launchGame.TabIndex = 2;
this.launchGame.Text = "Launch";
this.launchGame.UseVisualStyleBackColor = false;
this.launchGame.Click += new System.EventHandler(this.launchGame_Click);
this.launchGame.Click += new System.EventHandler(this.button2_Click);
//
// launchMessage
//
@ -204,7 +204,7 @@
//
this.spinner.Enabled = false;
this.spinner.Image = ((System.Drawing.Image)(resources.GetObject("spinner.Image")));
this.spinner.Location = new System.Drawing.Point(22, 66);
this.spinner.Location = new System.Drawing.Point(114, 67);
this.spinner.Name = "spinner";
this.spinner.Size = new System.Drawing.Size(26, 24);
this.spinner.TabIndex = 24;
@ -232,13 +232,13 @@
this.copy,
this.saveToFile});
this.outputRightClick.Name = "outputRightClick";
this.outputRightClick.Size = new System.Drawing.Size(167, 70);
this.outputRightClick.Size = new System.Drawing.Size(165, 70);
//
// selectAll
//
this.selectAll.Name = "selectAll";
this.selectAll.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.A)));
this.selectAll.Size = new System.Drawing.Size(166, 22);
this.selectAll.Size = new System.Drawing.Size(164, 22);
this.selectAll.Text = "Select All";
this.selectAll.Click += new System.EventHandler(this.selectAll_Click);
//
@ -247,14 +247,14 @@
this.copy.Name = "copy";
this.copy.ShortcutKeyDisplayString = "";
this.copy.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C)));
this.copy.Size = new System.Drawing.Size(166, 22);
this.copy.Size = new System.Drawing.Size(164, 22);
this.copy.Text = "Copy";
this.copy.Click += new System.EventHandler(this.copy_Click);
//
// saveToFile
//
this.saveToFile.Name = "saveToFile";
this.saveToFile.Size = new System.Drawing.Size(166, 22);
this.saveToFile.Size = new System.Drawing.Size(164, 22);
this.saveToFile.Text = "Save to file ...";
this.saveToFile.Click += new System.EventHandler(this.saveToFile_Click);
//

View file

@ -1,12 +1,16 @@
using Newtonsoft.Json;
using Microsoft.Win32;
using Newtonsoft.Json.Linq;
using PSLauncher.Properties;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Net.Http.Json;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@ -16,16 +20,12 @@ namespace PSLauncher
public enum LaunchDomain
{
Live,
PSForever,
Dev,
DevSSL
PSForever
}
public enum GameState
{
Stopped,
Authenticating,
Validating,
Launching,
Running,
Stopping
@ -33,59 +33,30 @@ namespace PSLauncher
public partial class LauncherForm : Form
{
private HttpClient httpClient;
private Process psProcess;
Process psProcess;
string USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0";
int DEFAULT_WEB_TIMEOUT = 5000;
bool bGameRunning = false;
int DEFAULT_WEB_TIMEOUT = 5;
GameState gameState = GameState.Stopped;
LaunchDomain domain = LaunchDomain.PSForever;
System.Drawing.Size oldSize = new System.Drawing.Size(0, 0);
LaunchDomain domain = LaunchDomain.Live;
Dictionary<LaunchDomain, string> domains = new Dictionary<LaunchDomain, string>()
{
{ LaunchDomain.Live, "https://lpj.daybreakgames.com/ps/live/" },
{ LaunchDomain.PSForever, "https://login.psforever.net/psf/live/" },
{ LaunchDomain.Dev, "http://localhost:9001/psf/live/" },
{ LaunchDomain.DevSSL, "http://localhost:9001/psf/live/" }
};
Dictionary<LaunchDomain, string> userAgents = new Dictionary<LaunchDomain, string>()
{
{ LaunchDomain.Live, "Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0" },
{ LaunchDomain.PSForever, string.Format("PSF Launcher v{0}", Program.launcherVersion) },
{ LaunchDomain.Dev, string.Format("PSF Launcher v{0}", Program.launcherVersion) },
{ LaunchDomain.DevSSL, string.Format("PSF Launcher v{0}", Program.launcherVersion) }
{ LaunchDomain.Live, "https://lpj.daybreakgames.com/ps/live" },
{ LaunchDomain.PSForever, "https://login.psforever.net/psf/live/login" }
};
public LauncherForm()
{
//
// init form components
//
InitializeComponent();
//
// init http client
//
httpClient = new HttpClient
{
BaseAddress = new Uri(domains[domain]),
Timeout = TimeSpan.FromSeconds(DEFAULT_WEB_TIMEOUT)
};
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(userAgents[domain]);
httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json");
//
// set form icon
//
this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath);
#if DEBUG
//Settings.Default.Reset();
Settings.Default.Reset();
Console.SetOut(new Util.ControlWriter(this.ps_consoleOutput));
#endif
skipLauncher.Checked = Settings.Default.SkipLauncher;
@ -110,7 +81,7 @@ namespace PSLauncher
private void LauncherForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (this.bGameRunning && Settings.Default.OutputShown)
if (this.bGameRunning)
{
DialogResult res = MessageBox.Show( "Are you sure you want to exit while managing PlanetSide PID " + psProcess.Id + "?" +
Environment.NewLine + "You won't see any debugging output if you do.", "Confirm exit", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
@ -128,14 +99,6 @@ namespace PSLauncher
gameState = GameState.Stopped;
}
private void startAuthenticating()
{
setButtonState(GameState.Authenticating);
progressShown(true);
gameState = GameState.Authenticating;
}
private void startLaunching()
{
setButtonState(GameState.Launching);
@ -150,48 +113,20 @@ namespace PSLauncher
{
switch (state)
{
case GameState.Stopped:
this.launchGame.BackColor = System.Drawing.Color.FromArgb(128, 255, 128);
this.launchGame.Enabled = true;
this.launchGame.Text = "Launch";
// allow modification if launcher is not skipped
if (!skipLauncher.Checked)
{
this.username.Enabled = true;
this.password.Enabled = true;
}
break;
case GameState.Authenticating:
this.launchGame.BackColor = System.Drawing.Color.FromArgb(128, 128, 255);
this.launchGame.Enabled = false;
this.launchGame.Text = "Authenticating";
// disallow modification because we are launching
this.username.Enabled = false;
this.password.Enabled = false;
break;
case GameState.Validating:
this.launchGame.BackColor = System.Drawing.Color.FromArgb(128, 128, 255);
this.launchGame.Enabled = false;
this.launchGame.Text = "Validating";
break;
case GameState.Launching:
this.launchGame.Enabled = false;
this.launchGame.Text = "Launching";
this.launchGame.Text = "Launching...";
break;
case GameState.Running:
this.launchGame.BackColor = System.Drawing.Color.FromArgb(255, 128, 128);
this.launchGame.Enabled = true;
this.launchGame.Text = "Kill";
break;
case GameState.Stopped:
this.launchGame.BackColor = System.Drawing.Color.FromArgb(128, 255, 128);
this.launchGame.Enabled = true;
this.launchGame.Text = "Launch";
break;
case GameState.Stopping:
this.launchGame.Enabled = false;
this.launchGame.Text = "Killing...";
@ -200,14 +135,6 @@ namespace PSLauncher
});
}
private void setButtonValidationState(int counter, int fileCount)
{
this.SafeInvoke(a =>
{
this.launchGame.Text = string.Format("Validating {0}/{1}", counter, fileCount);
});
}
private void setErrorMessage(string error)
{
this.SafeInvoke(a =>
@ -223,7 +150,7 @@ namespace PSLauncher
});
}
private void launchGame_Click(object sender, EventArgs e)
private void button2_Click(object sender, EventArgs e)
{
if(gameState == GameState.Running) // kill command
{
@ -256,36 +183,18 @@ namespace PSLauncher
// Build arguments
List<string> arguments = new List<string>();
if (skipLauncher.Checked)
arguments.Add("/K:StagingTest");
if (Settings.Default.CoreCombat)
arguments.Add("/CC");
if (Settings.Default.ExtraArgs != "")
arguments.Add(Settings.Default.ExtraArgs);
WriteClientINI();
if (skipLauncher.Checked)
{
arguments.Add("/K:StagingTest");
LaunchStaging();
}
else
{
Launch(arguments);
}
return;
//
// functions used only in this function
//
void WriteClientINI()
{
// Rewrite client.ini if selected
if (!Settings.Default.GenerateClientINI) return;
if(Settings.Default.GenerateClientINI)
{
string inipath = Path.Combine(Path.GetDirectoryName(psExe), "client.ini");
ClientINI ini = new ClientINI(inipath);
@ -301,7 +210,7 @@ namespace PSLauncher
}
}
void LaunchStaging()
if (skipLauncher.Checked)
{
// magic string to login to planetside from the actual game
if(!startPlanetSide(psExe, Path.GetDirectoryName(psExe), String.Join(" ", arguments)))
@ -313,14 +222,17 @@ namespace PSLauncher
gameRunning();
}
}
void Launch(List<string> _arguments)
else
{
startLaunching();
setErrorMessage("Login disabled. Use skip launcher");
gameStopped();
return;
/*startLaunching();
Task.Factory.StartNew(() =>
{
if (!this.doLogin(_arguments))
if(!this.doLogin())
{
gameStopped();
}
@ -328,478 +240,326 @@ namespace PSLauncher
{
gameRunning();
}
});
});*/
}
}
class DefaultResponse
HttpWebResponse netGetSession(string endpoint, CookieContainer cookies)
{
public int Status { get; set; }
};
string hostname = domains[domain];
HttpWebRequest req = WebRequest.Create(hostname + endpoint) as HttpWebRequest;
req.CookieContainer = cookies;
req.CookieContainer.MaxCookieSize = 4000;
req.Method = "GET";
req.UserAgent = USER_AGENT;
req.Timeout = DEFAULT_WEB_TIMEOUT;
class ErrorResponse : DefaultResponse
{
//[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public string ErrorText { get; set; }
return req.GetResponse() as HttpWebResponse;
}
class VersionResponse : DefaultResponse
bool doLogin()
{
public Int64 ReleaseDate { get; set; }
public string VersionString { get; set; }
}
long ts = (long)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
class TokenResponse : DefaultResponse
{
public string Token { get; set; }
}
string path = Settings.Default.PSPath;
string psExe = Path.Combine(path, SettingsForm.PS_EXE_NAME);
class ValidationResponse : DefaultResponse
{
public string[] Files { get; set; }
}
/////////////////////////////////////////////////////////////////
// Step 1: Establish Session ID
/////////////////////////////////////////////////////////////////
class GameTokenResponse : DefaultResponse
{
public string GameToken { get; set; }
}
String endpoint = domains[domain];
CookieContainer reqCookies = new CookieContainer();
HttpWebRequest req;
HttpWebResponse r;
class LoginRequestBody
{
public string Username { get; set; }
public string Password { get; set; }
public string Launcher { get; set; }
public int Mode { get; set; }
}
class ValidateRequestBody
{
public string Launcher { get; set; }
public string Files { get; set; }
}
void handleErrorResponse(ref string _errorBody)
{
var errorResponse = JsonConvert.DeserializeObject<ErrorResponse>(_errorBody);
string errorMessage;
addLine("=====");
addLine("The launcher received an error:");
switch(errorResponse.Status)
{
case 100:
errorMessage = "Launcher Update required.";
break;
case 101:
errorMessage = "Launcher is corrupted, please download the latest PSF Launcher.";
break;
case 102:
errorMessage = "Launcher token expired, please retry.";
break;
case 103:
errorMessage = "Gamefiles are corrupted, please replace them.";
break;
case 104:
errorMessage = "Launcher is no longer supported, please download the latest PSF Launcher.";
break;
case 200:
errorMessage = "This account does not yet support Launcher login, please sign in via \"Skip Launcher\" once.";
break;
case 201:
errorMessage = "Wrong Username or Password.";
break;
case 202:
errorMessage = "This account is inactive, please contact the PSF Support on Discord.";
break;
case 300:
errorMessage = "There has been a database error, please try again later.";
break;
default:
errorMessage = "Please report this error to the PSF Support on Discord\nError: " + errorResponse.Status;
break;
}
// print the launcher message
addLine(errorMessage);
// print the message from the error response
if ( errorResponse.ErrorText.Length != 0 )
{
addLine(errorResponse.ErrorText);
}
addLine("=====");
}
void checkLauncherVersion()
{
addLine("Checking for new PSF Launcher version...");
HttpResponseMessage respVersion;
try
{
respVersion = httpClient.GetAsync("version").Result;
r = netGetSession("/?t=43323", reqCookies);
}
catch
catch (WebException x)
{
addLine("Error: Version GET did not return");
return;
if (x.Status != WebExceptionStatus.TrustFailure)
{
addLine("Failed to gather initial session: " + x.Message);
return false;
}
if (!respVersion.IsSuccessStatusCode)
DialogResult res = MessageBox.Show("DBG's HTTPS certificate has failed to verify. This means that their certificate has expired " +
"or you may be getting Man-in-the-middled (attacked). If you are under attack, your credentials could be lost. Continue regardless?",
"Certificate error", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (res != DialogResult.Yes)
{
addLine("Error: Version GET status " + respVersion.StatusCode);
return;
return false;
}
var result = respVersion.Content.ReadAsStringAsync().Result;
var versionResponse = JsonConvert.DeserializeObject<VersionResponse>(result);
// WARNING: once we hit yes, all further SSL errors will be ignored
// https://stackoverflow.com/questions/2675133/c-sharp-ignore-certificate-errors
ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
if (versionResponse.Status != 0)
try
{
handleErrorResponse(ref result);
return;
r = netGetSession("/?t=43323", reqCookies);
}
var newestVersion = new List<string>(versionResponse.VersionString.Split('.'));
var localVersion = new List<string>(Program.launcherVersion.Split('.'));
if ( newestVersion.Count != localVersion.Count )
catch (WebException x2)
{
addLine("=====");
addLine("Could not compare launcher versions. Launching may fail.");
addLine(string.Format("Local version: v{0} - latest version: v{1}", localVersion, newestVersion));
addLine("Please update if there is a newer version of PSF Launcher.");
addLine("=====");
return;
}
bool newerVersionAvailable = false;
for (int i = 0; i < newestVersion.Count; i++)
{
var nv = int.Parse(newestVersion[i]);
var lv = int.Parse(localVersion[i]);
if (lv < nv)
{
newerVersionAvailable = true;
break;
addLine("Failed to gather initial session: " + x2.Message);
return false;
}
}
if ( newerVersionAvailable )
// Note: we must manually add secure cookies and CookieContainer is crap
// See http://thomaskrehbiel.com/post/1690-cookiecontainer_httpwebrequest_and_secure_cookies/
reqCookies.Add(new Uri(endpoint), r.Cookies);
addLine("PSWeb: session started");
r.Close();
/////////////////////////////////////////////////////////////////
// Step 2: Try logging in
/////////////////////////////////////////////////////////////////
req = WebRequest.Create(endpoint + "/login?t=43323") as HttpWebRequest;
req.CookieContainer = reqCookies;
req.Method = "POST";
req.UserAgent = USER_AGENT;
req.Timeout = DEFAULT_WEB_TIMEOUT;
req.Headers.Add("X-Requested-With", "XMLHttpRequest");
req.Headers.Add("Origin", "https://lp.soe.com");
req.ContentType = "application/x-www-form-urlencoded";
req.Referer = endpoint + "/?t=43323";
NameValueCollection query = new NameValueCollection();
query.Add("username", username.Text);
query.Add("password", password.Text);
query.Add("rememberPassword", "false");
query.Add("ts", ts.ToString());
var postdata = Encoding.ASCII.GetBytes(query.ToQueryString());
//addLine(query.ToQueryString());
req.ContentLength = postdata.Length;
using (var stream = req.GetRequestStream())
{
DateTime parsedTime = Util.UnixTimestampToDateTime(versionResponse.ReleaseDate);
stream.Write(postdata, 0, postdata.Length);
}
addLine("There is a newer version of the PSF Launcher. Launching may fail.");
try
{
r = req.GetResponse() as HttpWebResponse;
}
catch (WebException x)
{
string txt;
addLine(
string.Format(
"Version information: v{0} released {1}",
versionResponse.VersionString,
parsedTime.ToLocalTime().ToLongDateString()
)
);
addLine("=====");
using (HttpWebResponse respExcept = (HttpWebResponse)x.Response)
{
if (respExcept != null && respExcept.GetResponseStream().CanRead)
{
StreamReader r2 = new StreamReader(respExcept.GetResponseStream());
txt = r2.ReadToEnd();
respExcept.Close();
}
else
{
addLine("PSF Launcher is on the latest version.");
txt = "";
addLine("Login failed: " + x.Message);
return false;
}
}
addLine("");
string errorDetail = "";
return;
}
bool sendLoginData()
{
var username = this.username.Text;
var password = this.password.Text;
var passwordHash = Util.CalculateStringHash(EHashingAlgoType.SHA256, username + password);
var loginRequestBody = new LoginRequestBody
{
Username = this.username.Text,
Password = passwordHash,
Launcher = Program.launcherHash,
Mode = 0
};
HttpResponseMessage respLogin;
try
{
respLogin = httpClient.PostAsJsonAsync("login", loginRequestBody).Result;
JObject obj2 = JObject.Parse(txt);
errorDetail = (string)obj2["error"];
}
catch (Exception e)
catch (Newtonsoft.Json.JsonException x2)
{
addLine("Error: Login POST did not return");
addLine(e.InnerException.Message);
return false;
errorDetail = "Json parse error: " + x2.Message;
}
if (!respLogin.IsSuccessStatusCode)
if (errorDetail == "INVALID_ACCOUNT_ID") // not sure if we still get this...
{
addLine("Error: Login GET status " + respLogin.StatusCode);
return false;
setErrorMessage("Unknown username");
}
var result = respLogin.Content.ReadAsStringAsync().Result;
var loginResponse = JsonConvert.DeserializeObject<TokenResponse>(result);
if (loginResponse.Status != 0)
else if (errorDetail == "NEED_PASSWORD_RESET")
{
handleErrorResponse(ref result);
setErrorMessage("Your account needs a password reset");
}
else if (errorDetail == "BAD_LOGIN")
{
setErrorMessage("Bad password or username");
}
else // unrecognized!
{
setErrorMessage("Unknown error - see output window");
addLine("Login failure: " + x.Status);
addLine("Error: " + errorDetail);
addLine(txt);
}
return false;
}
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", loginResponse.Token);
if (!r.GetResponseStream().CanRead)
{
setErrorMessage("Unknown error - see output window");
addLine("No login response received");
addLine("Status: " + r.StatusCode);
return true;
return false;
}
bool getFilesToValidate(ref List<string> _filesToValidate)
{
HttpResponseMessage respValidateGet;
StreamReader reader = new StreamReader(r.GetResponseStream());
string text = reader.ReadToEnd();
//addLine(r.Headers["Set-Cookie"]);
reqCookies.Add(new Uri(endpoint), r.Cookies);
string result = "";
r.Close();
addLine("PSWeb: logged in");
try
{
respValidateGet = httpClient.GetAsync("validate").Result;
JObject obj = JObject.Parse(text);
result = (string)obj["result"];
}
catch (Exception e)
catch (Newtonsoft.Json.JsonException x2)
{
addLine("Error: Validate GET did not return");
addLine(e.Message);
return false;
result = "Json parse error: " + x2.Message;
}
if (!respValidateGet.IsSuccessStatusCode)
if (result != "SUCCESS")
{
addLine("Error: Validate GET status " + respValidateGet.StatusCode);
return false;
}
var result = respValidateGet.Content.ReadAsStringAsync().Result;
var validateGetResponse = JsonConvert.DeserializeObject<ValidationResponse>(result);
if (validateGetResponse.Status != 0)
{
handleErrorResponse(ref result);
setErrorMessage("Unknown error - see output window");
addLine("Bad login response: " + result);
addLine("Status: " + r.StatusCode);
addLine(text);
return false;
}
_filesToValidate.AddRange(validateGetResponse.Files);
return true;
}
/////////////////////////////////////////////////////////////////
// Step 3: Fetch the login token
/////////////////////////////////////////////////////////////////
bool validateFiles(List<string>_filesToValidate, ref string _fileHashResult)
{
string basePath = Settings.Default.PSPath;
List<string> fileHashes = new List<string>();
req = WebRequest.Create(endpoint + "/get_play_session?t=43323") as HttpWebRequest;
req.CookieContainer = reqCookies;
req.Method = "GET";
req.UserAgent = USER_AGENT;
req.Timeout = DEFAULT_WEB_TIMEOUT;
req.Headers.Add("Origin", "https://lp.soe.com");
req.Referer = endpoint + "/login?t=43323";
req.Headers.Add("X-Requested-With", "XMLHttpRequest");
req.Accept = "*/*";
for (int index = 0; index < _filesToValidate.Count; index++)
{
setButtonValidationState(index + 1, _filesToValidate.Count);
string filePath = _filesToValidate[index];
// get absolute path
var absPath = Path.GetFullPath(Path.Combine(basePath, filePath));
// make sure path is within planetside directory
if (!absPath.StartsWith(basePath))
{
addLine("Error: Filepath for validation is outside of planetside directory");
return false;
}
FileStream fileHandle;
try
{
fileHandle = File.OpenRead(absPath);
r = req.GetResponse() as HttpWebResponse;
}
catch
catch (WebException x)
{
addLine("Error: Required file not found: " + filePath);
return false;
}
string txt;
fileHashes.Add(Util.CalculateFileHash(fileHandle));
fileHandle.Close();
}
_fileHashResult = Util.CalculateStringHash(string.Join("", fileHashes));
return true;
}
bool sendValidationResult(string _hashResult)
using (HttpWebResponse respExcept = (HttpWebResponse)x.Response)
{
var validateResponseBody = new ValidateRequestBody
if (respExcept != null && respExcept.GetResponseStream().CanRead)
{
Launcher = Program.launcherHash,
Files = _hashResult
};
StreamReader r2 = new StreamReader(respExcept.GetResponseStream());
txt = r2.ReadToEnd();
}
else
{
txt = "";
}
}
string errorDetail = "";
HttpResponseMessage respValidatePost;
try
{
respValidatePost = httpClient.PostAsJsonAsync("validate", validateResponseBody).Result;
JObject obj2 = JObject.Parse(txt);
errorDetail = (string)obj2["result"];
}
catch
catch (Newtonsoft.Json.JsonException x2)
{
addLine("Error: Validate POST did not return");
return false;
errorDetail = "Json parse error: " + x2.Message;
}
if (!respValidatePost.IsSuccessStatusCode)
if (errorDetail == "RE_LOGIN")
{
addLine("Error: Validate POST status " + respValidatePost.StatusCode);
return false;
setErrorMessage("Failed to fetch token: bad login");
}
else // unrecognized!
{
setErrorMessage("Unknown error - see output window");
}
var result = respValidatePost.Content.ReadAsStringAsync().Result;
var validatePostResponse = JsonConvert.DeserializeObject<TokenResponse>(result);
if (validatePostResponse.Status != 0)
{
handleErrorResponse(ref result);
addLine("Get token failure: " + x.Status);
addLine("Error: " + errorDetail);
addLine(txt);
return false;
}
// update to validated token
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", validatePostResponse.Token);
if (!r.GetResponseStream().CanRead)
{
setErrorMessage("Unknown error - see output window");
addLine("No login response received");
addLine("Status: " + r.StatusCode);
return true;
return false;
}
bool getGameToken(ref string _gameToken)
{
HttpResponseMessage respGameToken;
reader = new StreamReader(r.GetResponseStream());
text = reader.ReadToEnd();
result = "";
r.Close();
string token = "";
try
{
respGameToken = httpClient.GetAsync("gametoken").Result;
JObject obj = JObject.Parse(text);
result = (string)obj["result"];
token = (string)obj["launchArgs"];
//addLine(text);
}
catch
catch (Newtonsoft.Json.JsonException x2)
{
addLine("Error: GameToken GET did not return");
return false;
result = "Json parse error: " + x2.Message;
token = "";
}
if (!respGameToken.IsSuccessStatusCode)
if (result != "SUCCESS")
{
addLine("Error: GameToken GET status " + respGameToken.StatusCode);
return false;
}
var result = respGameToken.Content.ReadAsStringAsync().Result;
var gameTokenResponse = JsonConvert.DeserializeObject<GameTokenResponse>(result);
if (gameTokenResponse.Status != 0)
{
handleErrorResponse(ref result);
setErrorMessage("Failed to get token");
addLine("Bad token response: " + result);
addLine("Status: " + r.StatusCode);
addLine(text);
return false;
}
_gameToken = gameTokenResponse.GameToken;
addLine("PSWeb: got launch args " + token);
return true;
}
string launch_args = token;
string ExtraLaunchArgs = Settings.Default.ExtraArgs;
bool doLogin(List<string> _arguments)
{
string path = Settings.Default.PSPath;
string psExe = Path.GetFullPath(Path.Combine(path, SettingsForm.PS_EXE_NAME));
if (ExtraLaunchArgs != String.Empty)
launch_args += " " + ExtraLaunchArgs;
// clear previous login token
httpClient.DefaultRequestHeaders.Authorization = null;
if ( !psExe.StartsWith(path) )
{
addLine("Error: planetside.exe outside of planetside directory: " + psExe);
return false;
}
addLine("");
addLine("Start launching");
addLine("");
// start authentication
setButtonState(GameState.Authenticating);
//
// get current launcher version
//
checkLauncherVersion();
//
// send login data
//
if ( !sendLoginData() )
{
return false;
}
//
// get file validation info
//
List<string> filesToValidate = new List<string>();
if ( !getFilesToValidate(ref filesToValidate) )
{
return false;
}
//
// validate files
//
setButtonState(GameState.Validating);
string hashResult = "";
if ( !validateFiles(filesToValidate, ref hashResult) )
{
return false;
}
//
// send validtation result
//
if ( !sendValidationResult(hashResult) )
{
return false;
}
// finished validating
setButtonState(GameState.Launching);
//
// get game token
//
string gameToken = "";
if ( !getGameToken(ref gameToken) )
{
return false;
}
_arguments.Add("/K:" + gameToken);
return startPlanetSide(psExe, path, String.Join(" ", _arguments));
return startPlanetSide(psExe, path, launch_args);
}
bool startPlanetSide(string exe, string workingDir, string args)
@ -816,11 +576,7 @@ namespace PSLauncher
psProcess.OutputDataReceived += new DataReceivedEventHandler(ps_OutputDataReceived);
psProcess.EnableRaisingEvents = true;
addLine(String.Format("ProcessStart: \"{0}\" {1}", exe, args));
addLine("");
addLine("LAUNCHING");
addLine("");
addLine("ProcessStart: \"" + exe + "\" " + args);
if (!psProcess.Start())
{
@ -882,11 +638,13 @@ namespace PSLauncher
{
this.spinner.Visible = true;
this.spinner.Enabled = true;
this.launchGame.Visible = false;
}
else
{
this.spinner.Visible = false;
this.spinner.Enabled = false;
this.launchGame.Visible = true;
}
});
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
@ -10,9 +10,8 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PSLauncher</RootNamespace>
<AssemblyName>PSForever_Launcher</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<IsWebBootstrapper>false</IsWebBootstrapper>
<NuGetPackageImportStamp>
@ -28,7 +27,7 @@
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.3.0.0</ApplicationVersion>
<ApplicationVersion>1.0.0.0</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
@ -38,21 +37,19 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\x86\Debug\</OutputPath>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\x86\Release\</OutputPath>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'LiveSupport|x86'">
<OutputPath>bin\x86\LiveSupport\</OutputPath>
@ -69,7 +66,6 @@
<CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
@ -86,11 +82,11 @@
<CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DefineConstants>
</DefineConstants>
<Optimize>true</Optimize>
<DebugType>none</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
@ -104,13 +100,12 @@
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
<UseVSHostingProcess>false</UseVSHostingProcess>
<WarningLevel>4</WarningLevel>
<WarningLevel>0</WarningLevel>
<CodeAnalysisIgnoreGeneratedCode>true</CodeAnalysisIgnoreGeneratedCode>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'LiveSupport|AnyCPU'">
<OutputPath>bin\LiveSupport\</OutputPath>
<DefineConstants>TRACE;HACKS</DefineConstants>
<DefineConstants>HACKS</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
@ -123,7 +118,6 @@
<CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
<CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
<CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>793DD622BA49EF7D3494640F6D9646D8D289304B</ManifestCertificateThumbprint>
@ -150,80 +144,14 @@
<PropertyGroup>
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'LiveSupport|x64'">
<OutputPath>bin\x64\LiveSupport\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<StartupObject>PSLauncher.Program</StartupObject>
</PropertyGroup>
<PropertyGroup>
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.7.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json">
<HintPath>..\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.Memory, Version=4.0.1.2, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Json, Version=7.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Http.Json.7.0.1\lib\net462\System.Net.Http.Json.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Text.Encodings.Web, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Encodings.Web.7.0.0\lib\net462\System.Text.Encodings.Web.dll</HintPath>
</Reference>
<Reference Include="System.Text.Json, Version=7.0.0.3, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Text.Json.7.0.3\lib\net462\System.Text.Json.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
@ -285,7 +213,6 @@
<DependentUpon>SettingsForm.cs</DependentUpon>
</EmbeddedResource>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="Properties\app.manifest" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
@ -298,9 +225,9 @@
</Compile>
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.8">
<BootstrapperPackage Include=".NETFramework,Version=v4.0,Profile=Client">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.8 %28x86 und x64%29</ProductName>
<ProductName>Microsoft .NET Framework 4 Client Profile %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
@ -320,88 +247,6 @@
<ItemGroup>
<Content Include="Resources\icon.ico" />
</ItemGroup>
<ItemGroup>
<PublishFile Include="Microsoft.Bcl.AsyncInterfaces">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>
</TargetPath>
<PublishState>Exclude</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>Assembly</FileType>
</PublishFile>
<PublishFile Include="System.Buffers">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>
</TargetPath>
<PublishState>Exclude</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>Assembly</FileType>
</PublishFile>
<PublishFile Include="System.Memory">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>
</TargetPath>
<PublishState>Exclude</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>Assembly</FileType>
</PublishFile>
<PublishFile Include="System.Numerics.Vectors">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>
</TargetPath>
<PublishState>Exclude</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>Assembly</FileType>
</PublishFile>
<PublishFile Include="System.Runtime.CompilerServices.Unsafe">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>
</TargetPath>
<PublishState>Exclude</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>Assembly</FileType>
</PublishFile>
<PublishFile Include="System.Text.Encodings.Web">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>
</TargetPath>
<PublishState>Exclude</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>Assembly</FileType>
</PublishFile>
<PublishFile Include="System.Threading.Tasks.Extensions">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>
</TargetPath>
<PublishState>Exclude</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>Assembly</FileType>
</PublishFile>
<PublishFile Include="System.ValueTuple">
<Visible>False</Visible>
<Group>
</Group>
<TargetPath>
</TargetPath>
<PublishState>Exclude</PublishState>
<IncludeHash>True</IncludeHash>
<FileType>Assembly</FileType>
</PublishFile>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -1,53 +1,25 @@
using PSLauncher.Properties;
using System;
using System.Diagnostics;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace PSLauncher
{
public enum EHashingAlgoType
{
MD5,
SHA1,
SHA256,
SHA384,
SHA512
}
static class Program
{
public static string launcherVersion;
public static string launcherHash;
public static EHashingAlgoType hashingAlgoType = EHashingAlgoType.SHA1;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
static void Main()
{
// prepare launcher info
{
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
launcherVersion = FileVersionInfo.GetVersionInfo(assemblyLocation).FileVersion;
launcherHash = Util.CalculateFileHash(assemblyLocation);
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Settings.Default.PropertyChanged += Default_PropertyChanged;
Application.Run(new LauncherForm());
// check if the launcher was called from the URI psf://
if (args.Length == 2 && args[0] == "--")
{
MessageBox.Show(args[1], args[0]);
}
}
static void Default_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)

View file

@ -1,4 +1,5 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@ -31,8 +32,8 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.3.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyVersion("1.2.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")]
// Stamp
// [assembly: AssemblyInformationalVersion("%version% [ %branch% @ %shorthash%%haschanges% ]")]

View file

@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
@ -13,13 +13,13 @@ namespace PSLauncher.Properties {
/// <summary>
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@ -33,7 +33,7 @@ namespace PSLauncher.Properties {
}
/// <summary>
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
@ -47,8 +47,8 @@ namespace PSLauncher.Properties {
}
/// <summary>
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
@ -61,7 +61,7 @@ namespace PSLauncher.Properties {
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap vector_logo_psforever {
get {

View file

@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
@ -12,7 +12,7 @@ namespace PSLauncher.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@ -61,7 +61,7 @@ namespace PSLauncher.Properties {
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool SkipLauncher {
get {
return ((bool)(this["SkipLauncher"]));
@ -95,6 +95,20 @@ namespace PSLauncher.Properties {
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n<ArrayOfString xmlns:xsi=\"http://www.w3." +
"org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n <s" +
"tring>PSForever,play.psforever.net,51000</string>\r\n</ArrayOfString>")]
public global::System.Collections.Specialized.StringCollection ServerList {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["ServerList"]));
}
set {
this["ServerList"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
@ -118,19 +132,5 @@ namespace PSLauncher.Properties {
this["ServerSelection"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n<ArrayOfString xmlns:xsi=\"http://www.w3." +
"org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\r\n <s" +
"tring>PSForever,play.psforever.net,51000</string>\r\n</ArrayOfString>")]
public global::System.Collections.Specialized.StringCollection ServerList {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["ServerList"]));
}
set {
this["ServerList"] = value;
}
}
}
}

View file

@ -12,7 +12,7 @@
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="SkipLauncher" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="ClearOutputOnLaunch" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
@ -20,17 +20,17 @@
<Setting Name="CoreCombat" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="GenerateClientINI" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ServerSelection" Type="System.Int32" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="ServerList" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)">&lt;?xml version="1.0" encoding="utf-16"?&gt;
&lt;ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
&lt;string&gt;PSForever,play.psforever.net,51000&lt;/string&gt;
&lt;/ArrayOfString&gt;</Value>
</Setting>
<Setting Name="GenerateClientINI" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ServerSelection" Type="System.Int32" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
</Settings>
</SettingsFile>

View file

@ -8,7 +8,6 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography;
namespace PSLauncher
{
@ -166,58 +165,6 @@ namespace PSLauncher
return Environment.GetEnvironmentVariable("ProgramFiles");
}
public static string CalculateFileHash(string _filePath)
{
using (var hashingAlgorithm = HashAlgorithm.Create(Program.hashingAlgoType.ToString()))
{
using (var fileHandle = File.OpenRead(_filePath))
{
return CalculateFileHash(hashingAlgorithm, fileHandle);
}
}
}
public static string CalculateFileHash(FileStream _fileHandle)
{
using (var hashingAlgorithm = HashAlgorithm.Create(Program.hashingAlgoType.ToString()))
{
return CalculateFileHash(hashingAlgorithm, _fileHandle);
}
}
private static string CalculateFileHash(HashAlgorithm _hashingAlgorithm, FileStream _fileHandle)
{
const int bufferSize = 1 * 1000 * 1000; // 1MB
// read file in bigger buffer to improve hashing performance (slightly)
using (var stream2 = new BufferedStream(_fileHandle, bufferSize))
{
return BitConverter.ToString(_hashingAlgorithm.ComputeHash(stream2)).Replace("-", "").ToLower();
}
}
public static string CalculateStringHash(string _string)
{
using (var hashingAlgorithm = HashAlgorithm.Create(Program.hashingAlgoType.ToString()))
{
return CalculateStringHash(hashingAlgorithm, _string);
}
}
public static string CalculateStringHash(EHashingAlgoType _algoType, string _string)
{
using (var hashingAlgorithm = HashAlgorithm.Create(_algoType.ToString()))
{
return CalculateStringHash(hashingAlgorithm, _string);
}
}
private static string CalculateStringHash(HashAlgorithm _hashAlgorithm, string _string)
{
byte[] stringBytes = System.Text.Encoding.ASCII.GetBytes(_string);
return BitConverter.ToString(_hashAlgorithm.ComputeHash(stringBytes)).Replace("-", "").ToLower();
}
// from https://stackoverflow.com/questions/18726852/redirecting-console-writeline-to-textbox
public class ControlWriter : TextWriter
{
@ -242,15 +189,5 @@ namespace PSLauncher
get { return Encoding.ASCII; }
}
}
// https://stackoverflow.com/a/250400
public static DateTime UnixTimestampToDateTime( double unixTimestmap )
{
// Unix timestamp is seconds past epoch
DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
dateTime = dateTime.AddSeconds(unixTimestmap).ToLocalTime();
return dateTime;
}
}
}

View file

@ -17,7 +17,7 @@
<value>False</value>
</setting>
<setting name="SkipLauncher" serializeAs="String">
<value>True</value>
<value>False</value>
</setting>
<setting name="ClearOutputOnLaunch" serializeAs="String">
<value>True</value>
@ -25,12 +25,6 @@
<setting name="CoreCombat" serializeAs="String">
<value>True</value>
</setting>
<setting name="GenerateClientINI" serializeAs="String">
<value>True</value>
</setting>
<setting name="ServerSelection" serializeAs="String">
<value>0</value>
</setting>
<setting name="ServerList" serializeAs="Xml">
<value>
<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@ -39,19 +33,12 @@
</ArrayOfString>
</value>
</setting>
<setting name="GenerateClientINI" serializeAs="String">
<value>True</value>
</setting>
<setting name="ServerSelection" serializeAs="String">
<value>0</value>
</setting>
</PSLauncher.Properties.Settings>
</userSettings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" /></startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.0.0.3" newVersion="7.0.0.3" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl.AsyncInterfaces" version="7.0.0" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Memory" version="4.5.5" targetFramework="net48" />
<package id="System.Net.Http.Json" version="7.0.1" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net48" />
<package id="System.Text.Encodings.Web" version="7.0.0" targetFramework="net48" />
<package id="System.Text.Json" version="7.0.3" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
</packages>

View file

@ -1,14 +1,9 @@
PlanetSide 1 GameLauncher
=========================
The frontend C# GUI for a simple PlanetSide 1 (PS1) launcher.
It automatically rewrites PlanetSide's `client.ini` file to enable users to quickly switch between servers.
Created to assist the [PSForever](http://psforever.net/) project.
![Imgur](http://i.imgur.com/122GpvC.png)
![Imgur](http://i.imgur.com/cOF69eO.png)
Notable Features
================
* Skip launcher (login through PlanetSide UI)

View file

@ -1,42 +0,0 @@
#!/bin/sh
set -eu
# arg: prompt
ask() {
read -p "$1" choice
}
RELEASE_DIR="../Release"
BIN_DIR="../PSLauncher/bin/Release"
ask "Version: "
VERSION=$choice
echo "Making release for version $VERSION"
OUTPUT_FOLDER="PSForever_Launcher_$VERSION"
OUTPUT_PATH="${RELEASE_DIR}/${OUTPUT_FOLDER}"
mkdir -p ${OUTPUT_PATH}
# Copy files
cp "$BIN_DIR/"*.dll "$BIN_DIR/"*.exe ${OUTPUT_PATH}
cp "../README.md" ${OUTPUT_PATH}
mv "${OUTPUT_PATH}/README.md" "${OUTPUT_PATH}/README.txt"
echo "==== Releasing contents ===="
ls -la ${OUTPUT_PATH}
echo "============================"
# ZIP release
ZIP_FILE="${OUTPUT_FOLDER}.zip"
echo "ZIPing release to ${ZIP_FILE}"
cd ${RELEASE_DIR}
zip -r "${ZIP_FILE}" "${OUTPUT_FOLDER}"
echo "ZIP done"
ls -lh "${ZIP_FILE}"
echo "Release complete!"
exit 0