initial commit

This commit is contained in:
flash 2023-10-08 02:10:21 +02:00
commit 880f9f299d
23 changed files with 4785 additions and 0 deletions

63
.gitattributes vendored Normal file
View file

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

215
.gitignore vendored Normal file
View file

@ -0,0 +1,215 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
MidiPlayer/build.txt
MwSignTool/public.xml
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml

13
README.md Normal file
View file

@ -0,0 +1,13 @@
# SoFii
A prelaunch environment for Soldier of Fortune 2, primary for use with the Flashii server of it.
The tool lets you decide what video mode to use before launching and also provides you with an easy way to set a coloured player name.
This tool doesn't download the game for you, a copy of it with SoF2MP.exe is required!
The only network request this program makes is a TXT DNS lookup to `_sofii.flashii.net` to retrieve the current server address, whatever the latest recommended version of the tool is and if needed an announcement message.
This network request will not happen if "Use custom server address" is checked.
Settings are stored in the Windows Registry at `HKCU\\Software\\flash.moe\\SoFii`.
If for whatever reason you want to prevent the program from ever doing the DNS lookup create a DWORD value called `UseDefaultServer` and set it to `0`.

25
SoFii.sln Normal file
View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.34031.81
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoFii", "SoFii\SoFii.csproj", "{6026586C-8730-428A-8E61-E75568FF7185}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{6026586C-8730-428A-8E61-E75568FF7185}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6026586C-8730-428A-8E61-E75568FF7185}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6026586C-8730-428A-8E61-E75568FF7185}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6026586C-8730-428A-8E61-E75568FF7185}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A304B1B1-038C-4716-9CCD-8255021744FA}
EndGlobalSection
EndGlobal

31
SoFii/ColourCharInfo.cs Normal file
View file

@ -0,0 +1,31 @@
using System;
using System.Drawing;
namespace SoFii {
public readonly struct ColourCharInfo {
public readonly char Char;
public readonly Color Colour;
public ColourCharInfo(char chr, Color col) {
Char = chr;
Colour = col;
}
public static ColourCharInfo FromStrings(string chr, string col) {
if(chr is null)
throw new ArgumentNullException(nameof(chr));
if(col is null)
throw new ArgumentNullException(nameof(col));
if(chr.Length < 1)
throw new ArgumentException("chr must contain at least 1 character", nameof(chr));
if(!int.TryParse(col, out int colourRaw))
throw new ArgumentException("col is not a valid integer", nameof(col));
return new ColourCharInfo(chr[0], Color.FromArgb(unchecked(colourRaw | (int)0xFF000000)));
}
public override string ToString() {
return $"ColourCharInfo [Char={Char}, Colour={Colour}]";
}
}
}

6
SoFii/Constants.cs Normal file
View file

@ -0,0 +1,6 @@
namespace SoFii {
public static class Constants {
public const string SOFII_DOMAIN = "_sofii.flashii.net.";
public const string INFO_URL = "https://fii.moe/sofii";
}
}

404
SoFii/DNSClient.cs Normal file
View file

@ -0,0 +1,404 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace SoFii {
public class DNSClient : IDisposable {
public static readonly string[] Servers;
static DNSClient() {
string[] servers = Settings.CustomDNSServers;
if(servers.Length < 1 || string.IsNullOrEmpty(servers[0]))
servers = EmbeddedResources.GetDNSServers();
RNG.Shuffle(servers);
Servers = servers;
}
private static int CurrentServerIndex = -1;
public static string GetNextCommonServer() {
Interlocked.Increment(ref CurrentServerIndex);
return Servers[CurrentServerIndex % Servers.Length];
}
private readonly Socket Sock;
public DNSClient() : this(GetNextCommonServer()) { }
public DNSClient(string ipAddr) : this(
IPAddress.Parse(ipAddr ?? throw new ArgumentNullException(nameof(ipAddr)))
) { }
public DNSClient(IPAddress ipAddr) {
if(ipAddr == null)
throw new ArgumentNullException(nameof(ipAddr));
Sock = new Socket(ipAddr.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
Sock.Connect(ipAddr, 53);
}
public Response QueryRecords(RecordType qType, string qName) {
if(qName == null)
throw new ArgumentNullException(nameof(qName));
if(string.IsNullOrEmpty(qName))
throw new ArgumentException("domain may not be empty", nameof(qName));
byte[] transId = new byte[2];
RNG.NextBytes(transId);
using(MemoryStream ms = new MemoryStream()) {
// Transaction ID
ms.Write(transId, 0, 2);
// QR, Opcode, AC, TC, RD
ms.WriteByte(0x01); // RD = Recursion Desired, we want this
// RA, Z, RCODE
ms.WriteByte(0);
// QDCOUNT (1 query)
ms.WriteByte(0);
ms.WriteByte(0x01);
// ANCOUNT
ms.WriteByte(0);
ms.WriteByte(0);
// NSCOUNT
ms.WriteByte(0);
ms.WriteByte(0);
// ARCOUNT
ms.WriteByte(0);
ms.WriteByte(0);
// QNAME
string[] qNameParts = qName.Split('.');
for(int i = 0; i < qNameParts.Length; ++i) {
string qNamePart = qNameParts[i];
int qNamePartLength = Encoding.ASCII.GetByteCount(qNamePart);
if(i != (qNameParts.Length - 1) && qNamePartLength < 1)
throw new ArgumentException("Malformed domain (label too short)", nameof(qName));
if(qNamePartLength > 0x3F)
throw new ArgumentException("Malformed domain (label too long)", nameof(qName));
ms.WriteByte((byte)qNamePartLength);
if(qNamePartLength > 0)
ms.Write(Encoding.ASCII.GetBytes(qNamePart), 0, qNamePartLength);
}
// QTYPE
int qTypeNum = (int)qType;
ms.WriteByte((byte)(qTypeNum >> 8));
ms.WriteByte((byte)qTypeNum);
// QCLASS - Interneet Protocol
ms.WriteByte(0);
ms.WriteByte(0x01);
// Sende
int sent = Sock.Send(ms.ToArray());
if(sent != ms.Length)
throw new DNSClientException("Was unable send whole packet");
}
int read;
byte[] buffer = new byte[1024];
int error;
List<Query> queries = new List<Query>();
List<Answer> answers = new List<Answer>();
using(MemoryStream ms = new MemoryStream()) {
read = Sock.Receive(buffer);
if(read < 12)
throw new DNSClientException("Did not receive a full DNS response header");
ms.Write(buffer, 0, read);
while(read >= buffer.Length) {
read = Sock.Receive(buffer);
ms.Write(buffer, 0, read);
}
ms.Seek(0, SeekOrigin.Begin);
if(ms.ReadByte() != transId[0] || ms.ReadByte() != transId[1])
throw new DNSClientException("Response received did not contain the same transaction ID");
int flags = (ms.ReadByte() << 8) | ms.ReadByte();
if((flags & 0x8000) == 0)
throw new DNSClientException("Response did not have the response bit set");
if((flags & 0x0200) > 0)
throw new DNSClientException("Truncated responses are not supported");
if((flags & 0x0080) == 0)
throw new DNSClientException("This server does not allow recursion");
error = flags & 0x000F;
if(error == 1)
throw new DNSClientException("Request was incorrectly formatted");
if(error == 2)
throw new DNSClientException("Name server was unable to process the query");
if(error == 4)
throw new DNSClientException("Name server does not support this type of query");
if(error == 5)
throw new DNSClientException("Name server refused to respond to this query");
if(error != 0 && error != 3)
throw new DNSClientException($"An error occurred while processing this query: {error}");
int qdCount = (ms.ReadByte() << 8) | ms.ReadByte();
int anCount = (ms.ReadByte() << 8) | ms.ReadByte();
int nsCount = (ms.ReadByte() << 8) | ms.ReadByte();
int arCount = (ms.ReadByte() << 8) | ms.ReadByte();
for(int i = 0; i < qdCount; ++i) {
string qdName = string.Join(".", ReadLabels(ms));
RecordType qdType = (RecordType)((ms.ReadByte() << 8) | ms.ReadByte());
int qdClass = (ms.ReadByte() << 8) | ms.ReadByte();
queries.Add(new Query(qdName, qdType, qdClass));
}
for(int i = 0; i < anCount; ++i) {
string anName = string.Join(".", ReadLabels(ms));
RecordType anType = (RecordType)((ms.ReadByte() << 8) | ms.ReadByte());
int anClass = (ms.ReadByte() << 8) | ms.ReadByte();
int anTTL = (ms.ReadByte() << 24) | (ms.ReadByte() << 16) | (ms.ReadByte() << 8) | ms.ReadByte();
int anDataLength = (ms.ReadByte() << 8) | ms.ReadByte();
byte[] anData;
if(anDataLength < 1) {
#if NETFX4_6_OR_GREATER || NETCOREAPP1_0_OR_GREATER
anData = Array.Empty<byte>();
#else
anData = new byte[0];
#endif
} else {
anData = new byte[anDataLength];
read = ms.Read(anData, 0, anDataLength);
if(read != anDataLength)
throw new DNSClientException("Could not read data field of record");
}
answers.Add(new Answer(anName, anType, anClass, anTTL, anDataLength, anData));
}
}
return new Response(
error,
queries.ToArray(),
answers.ToArray()
);
}
private static string[] ReadLabels(Stream stream) {
int length = stream.ReadByte();
bool isRef = (length & 0xC0) == 0xC0;
long jumpTo = 0;
length &= 0x3F;
if(isRef) {
length <<= 8;
length |= stream.ReadByte();
jumpTo = stream.Position;
stream.Seek(length, SeekOrigin.Begin);
length = stream.ReadByte();
}
byte[] buffer = new byte[0x3F];
List<string> labels = new List<string>();
int read;
for(; ; )
{
if(length < 1) {
labels.Add(string.Empty);
break;
}
if(length > 0x3F)
throw new DNSClientException("Received label field that claims to be longer than 63 bytes");
read = stream.Read(buffer, 0, length);
if(read != length)
throw new DNSClientException("Wasn't able to read the entire label (end of stream?)");
labels.Add(Encoding.ASCII.GetString(buffer, 0, read));
length = stream.ReadByte();
}
if(isRef)
stream.Seek(jumpTo, SeekOrigin.Begin);
return labels.ToArray();
}
public struct Response {
public int Status;
public Query[] Queries;
public Answer[] Answers;
public bool IsSuccess => Status == 0;
public Response(
int status,
Query[] queries,
Answer[] answers
) {
Status = status;
Queries = queries;
Answers = answers;
}
}
public struct Query {
public string Name;
public RecordType Type;
public int Class;
public Query(
string name,
RecordType type,
int @class
) {
Name = name;
Type = type;
Class = @class;
}
public override string ToString() {
return $"{Name} {Type} {Class}";
}
}
public struct Answer {
public string Name;
public RecordType Type;
public int Class;
public int TTL;
public int DataLength;
public byte[] Data;
public Answer(
string name,
RecordType type,
int @class,
int ttl,
int dataLength,
byte[] data
) {
Name = name;
Type = type;
Class = @class;
TTL = ttl;
DataLength = dataLength;
Data = data;
}
// TODO: support other record types someday maybe
public AnswerTXT GetTXTData() {
return new AnswerTXT(Data);
}
public override string ToString() {
return $"{Name} {Type} {Class} {TTL} {DataLength} {Encoding.ASCII.GetString(Data)}";
}
public struct AnswerTXT {
public int Length;
public string Text;
public AnswerTXT(byte[] data) {
Length = data[0];
Text = Encoding.ASCII.GetString(data, 1, Length);
}
}
}
public enum RecordType : ushort {
A = 0x0001,
NS = 0x0002,
CNAME = 0x0005,
SOA = 0x0006,
PTR = 0x000C,
HINFO = 0x000D,
MX = 0x000F,
TXT = 0x0010,
RP = 0x0011,
AFSDB = 0x0012,
SIG = 0x0018,
KEY = 0x0019,
AAAA = 0x001C,
LOC = 0x001D,
SRV = 0x0021,
NAPTR = 0x0023,
KX = 0x0024,
CERT = 0x0025,
DNAME = 0x0027,
OPT = 0x0029,
APL = 0x002A,
DS = 0x002B,
SSHFP = 0x002C,
IPSECKEY = 0x002D,
RRSIG = 0x002E,
NSEC = 0x002F,
DNSKEY = 0x0030,
DHCID = 0x0031,
NSEC3 = 0x0032,
NSEC3PARAM = 0x0033,
TLSA = 0x0034,
SMIMEA = 0x0035,
HIP = 0x0037,
CDS = 0x003B,
CDNSKEY = 0x003C,
OPENPGPKEY = 0x003D,
CSYNC = 0x003E,
ZONEMD = 0x003F,
SVCB = 0x0040,
HTTPS = 0x0041,
EUI48 = 0x006C,
EUI64 = 0x006D,
TKEY = 0x00F9,
TSIG = 0x00FA,
IXFR = 0x00FB,
AXFR = 0x00FC,
ANY = 0x00FF,
URI = 0x0100,
CAA = 0x0101,
TA = 0x8000,
DLV = 0x8001,
}
private bool IsDisposed;
~DNSClient() {
DoDispose();
}
public void Dispose() {
DoDispose();
GC.SuppressFinalize(this);
}
private void DoDispose() {
if(IsDisposed)
return;
IsDisposed = true;
Sock?.Close();
}
}
public class DNSClientException : Exception {
public DNSClientException() : base() { }
public DNSClientException(string message) : base(message) { }
public DNSClientException(string message, Exception innerException) : base(message, innerException) { }
}
}

15
SoFii/DNSServers.txt Normal file
View file

@ -0,0 +1,15 @@
# OpenDNS
208.67.222.222
208.67.220.220
# CloudFlare
1.1.1.1
1.0.0.1
# Google Public DNS
8.8.8.8
8.8.4.4
# Quad9
9.9.9.9
149.112.112.112
# Control D
76.76.2.0
76.76.10.0

3
SoFii/Delegates.cs Normal file
View file

@ -0,0 +1,3 @@
namespace SoFii {
public delegate void Action();
}

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace SoFii {
public static class EmbeddedResources {
public static void LoadLines(string name, Action<string> onLine) {
using(Stream stream = Program.CurrentAssembly.GetManifestResourceStream($@"{Program.CurrentAssemblyName.Name}.{name}"))
using(StreamReader sr = new StreamReader(stream, true)) {
string line;
while(!string.IsNullOrEmpty(line = sr.ReadLine()))
onLine(line);
}
}
public static string[] GetDNSServers() {
List<string> items = new List<string>();
LoadLines("DNSServers.txt", line => {
if(!line.StartsWith("#"))
items.Add(line.Trim());
});
return items.ToArray();
}
public static ColourCharInfo[] GetColourChars() {
List<ColourCharInfo> items = new List<ColourCharInfo>();
LoadLines("NameColours.txt", line => {
string[] parts = line.Split(' ');
if(parts.Length > 1)
items.Add(ColourCharInfo.FromStrings(parts[0], parts[1]));
});
return items.ToArray();
}
}
}

402
SoFii/MainWindow.Designer.cs generated Normal file
View file

@ -0,0 +1,402 @@

namespace SoFii
{
partial class MainWindow
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));
this.serverBox = new System.Windows.Forms.GroupBox();
this.serverAddressLabel = new System.Windows.Forms.Label();
this.serverAddressInput = new System.Windows.Forms.TextBox();
this.serverResetButton = new System.Windows.Forms.Button();
this.launchGameButton = new System.Windows.Forms.Button();
this.gameBox = new System.Windows.Forms.GroupBox();
this.gameLocationLabel = new System.Windows.Forms.Label();
this.gameLocationInput = new System.Windows.Forms.TextBox();
this.gameBrowseButton = new System.Windows.Forms.Button();
this.mainTabs = new System.Windows.Forms.TabControl();
this.locationsTabPage = new System.Windows.Forms.TabPage();
this.versionLabel = new System.Windows.Forms.Label();
this.mainOpenFileDiag = new System.Windows.Forms.OpenFileDialog();
this.serverUseCustom = new System.Windows.Forms.CheckBox();
this.playerTabPage = new System.Windows.Forms.TabPage();
this.playerNameBox = new System.Windows.Forms.GroupBox();
this.playerNameLabel = new System.Windows.Forms.Label();
this.playerNameInput = new System.Windows.Forms.TextBox();
this.playerNameColoursBox = new System.Windows.Forms.GroupBox();
this.playerNameColourButton = new System.Windows.Forms.Button();
this.playerNamePreview = new System.Windows.Forms.Panel();
this.gfxBox = new System.Windows.Forms.GroupBox();
this.gfxFullscreen = new System.Windows.Forms.CheckBox();
this.gfxResolutionLabel = new System.Windows.Forms.Label();
this.gfxResolutionSelect = new System.Windows.Forms.ComboBox();
this.serverBox.SuspendLayout();
this.gameBox.SuspendLayout();
this.mainTabs.SuspendLayout();
this.locationsTabPage.SuspendLayout();
this.playerTabPage.SuspendLayout();
this.playerNameBox.SuspendLayout();
this.playerNameColoursBox.SuspendLayout();
this.gfxBox.SuspendLayout();
this.SuspendLayout();
//
// serverBox
//
this.serverBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.serverBox.Controls.Add(this.serverUseCustom);
this.serverBox.Controls.Add(this.serverAddressLabel);
this.serverBox.Controls.Add(this.serverAddressInput);
this.serverBox.Controls.Add(this.serverResetButton);
this.serverBox.Location = new System.Drawing.Point(6, 65);
this.serverBox.Name = "serverBox";
this.serverBox.Size = new System.Drawing.Size(540, 71);
this.serverBox.TabIndex = 300;
this.serverBox.TabStop = false;
this.serverBox.Text = "Server";
//
// serverAddressLabel
//
this.serverAddressLabel.AutoSize = true;
this.serverAddressLabel.Location = new System.Drawing.Point(36, 24);
this.serverAddressLabel.Name = "serverAddressLabel";
this.serverAddressLabel.Size = new System.Drawing.Size(45, 13);
this.serverAddressLabel.TabIndex = 301;
this.serverAddressLabel.Text = "Address";
//
// serverAddressInput
//
this.serverAddressInput.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.serverAddressInput.Location = new System.Drawing.Point(87, 21);
this.serverAddressInput.Name = "serverAddressInput";
this.serverAddressInput.Size = new System.Drawing.Size(360, 20);
this.serverAddressInput.TabIndex = 302;
//
// serverResetButton
//
this.serverResetButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.serverResetButton.Location = new System.Drawing.Point(453, 19);
this.serverResetButton.Name = "serverResetButton";
this.serverResetButton.Size = new System.Drawing.Size(75, 23);
this.serverResetButton.TabIndex = 303;
this.serverResetButton.Text = "Reset";
this.serverResetButton.UseVisualStyleBackColor = true;
this.serverResetButton.Click += new System.EventHandler(this.serverResetButton_Click);
//
// launchGameButton
//
this.launchGameButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.launchGameButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.launchGameButton.Location = new System.Drawing.Point(448, 526);
this.launchGameButton.Name = "launchGameButton";
this.launchGameButton.Size = new System.Drawing.Size(124, 23);
this.launchGameButton.TabIndex = 11;
this.launchGameButton.Text = "Launch SoF2";
this.launchGameButton.UseVisualStyleBackColor = true;
this.launchGameButton.Click += new System.EventHandler(this.launchGameButton_Click);
//
// gameBox
//
this.gameBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.gameBox.Controls.Add(this.gameLocationLabel);
this.gameBox.Controls.Add(this.gameLocationInput);
this.gameBox.Controls.Add(this.gameBrowseButton);
this.gameBox.Location = new System.Drawing.Point(6, 6);
this.gameBox.Name = "gameBox";
this.gameBox.Size = new System.Drawing.Size(540, 53);
this.gameBox.TabIndex = 200;
this.gameBox.TabStop = false;
this.gameBox.Text = "Game";
//
// gameLocationLabel
//
this.gameLocationLabel.AutoSize = true;
this.gameLocationLabel.Location = new System.Drawing.Point(33, 23);
this.gameLocationLabel.Name = "gameLocationLabel";
this.gameLocationLabel.Size = new System.Drawing.Size(48, 13);
this.gameLocationLabel.TabIndex = 201;
this.gameLocationLabel.Text = "Location";
//
// gameLocationInput
//
this.gameLocationInput.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.gameLocationInput.Enabled = false;
this.gameLocationInput.Location = new System.Drawing.Point(87, 20);
this.gameLocationInput.Name = "gameLocationInput";
this.gameLocationInput.Size = new System.Drawing.Size(360, 20);
this.gameLocationInput.TabIndex = 202;
//
// gameBrowseButton
//
this.gameBrowseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.gameBrowseButton.Location = new System.Drawing.Point(453, 18);
this.gameBrowseButton.Name = "gameBrowseButton";
this.gameBrowseButton.Size = new System.Drawing.Size(75, 23);
this.gameBrowseButton.TabIndex = 203;
this.gameBrowseButton.Text = "Browse...";
this.gameBrowseButton.UseVisualStyleBackColor = true;
this.gameBrowseButton.Click += new System.EventHandler(this.gameBrowseButton_Click);
//
// mainTabs
//
this.mainTabs.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.mainTabs.Controls.Add(this.locationsTabPage);
this.mainTabs.Controls.Add(this.playerTabPage);
this.mainTabs.Location = new System.Drawing.Point(12, 12);
this.mainTabs.Name = "mainTabs";
this.mainTabs.SelectedIndex = 0;
this.mainTabs.Size = new System.Drawing.Size(560, 508);
this.mainTabs.TabIndex = 12;
this.mainTabs.SelectedIndexChanged += new System.EventHandler(this.mainTabs_SelectedIndexChanged);
//
// locationsTabPage
//
this.locationsTabPage.Controls.Add(this.gameBox);
this.locationsTabPage.Controls.Add(this.serverBox);
this.locationsTabPage.Location = new System.Drawing.Point(4, 22);
this.locationsTabPage.Name = "locationsTabPage";
this.locationsTabPage.Padding = new System.Windows.Forms.Padding(3);
this.locationsTabPage.Size = new System.Drawing.Size(552, 482);
this.locationsTabPage.TabIndex = 0;
this.locationsTabPage.Text = "Locations";
this.locationsTabPage.UseVisualStyleBackColor = true;
//
// versionLabel
//
this.versionLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.versionLabel.AutoSize = true;
this.versionLabel.ForeColor = System.Drawing.SystemColors.GrayText;
this.versionLabel.Location = new System.Drawing.Point(13, 533);
this.versionLabel.Name = "versionLabel";
this.versionLabel.Size = new System.Drawing.Size(117, 13);
this.versionLabel.TabIndex = 13;
this.versionLabel.Text = "SoFii version goes here";
//
// mainOpenFileDiag
//
this.mainOpenFileDiag.DefaultExt = "exe";
this.mainOpenFileDiag.Filter = "SoF2 Multiplayer Executable|SoF2MP.exe";
this.mainOpenFileDiag.RestoreDirectory = true;
//
// serverUseCustom
//
this.serverUseCustom.AutoSize = true;
this.serverUseCustom.Location = new System.Drawing.Point(87, 47);
this.serverUseCustom.Name = "serverUseCustom";
this.serverUseCustom.Size = new System.Drawing.Size(154, 17);
this.serverUseCustom.TabIndex = 304;
this.serverUseCustom.Text = "Use custom server address";
this.serverUseCustom.UseVisualStyleBackColor = true;
this.serverUseCustom.CheckedChanged += new System.EventHandler(this.serverUseCustom_CheckedChanged);
//
// playerTabPage
//
this.playerTabPage.Controls.Add(this.gfxBox);
this.playerTabPage.Controls.Add(this.playerNameBox);
this.playerTabPage.Location = new System.Drawing.Point(4, 22);
this.playerTabPage.Name = "playerTabPage";
this.playerTabPage.Padding = new System.Windows.Forms.Padding(3);
this.playerTabPage.Size = new System.Drawing.Size(552, 482);
this.playerTabPage.TabIndex = 1;
this.playerTabPage.Text = "Settings";
this.playerTabPage.UseVisualStyleBackColor = true;
//
// playerNameBox
//
this.playerNameBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.playerNameBox.Controls.Add(this.playerNamePreview);
this.playerNameBox.Controls.Add(this.playerNameColoursBox);
this.playerNameBox.Controls.Add(this.playerNameLabel);
this.playerNameBox.Controls.Add(this.playerNameInput);
this.playerNameBox.Location = new System.Drawing.Point(6, 6);
this.playerNameBox.Name = "playerNameBox";
this.playerNameBox.Size = new System.Drawing.Size(540, 360);
this.playerNameBox.TabIndex = 400;
this.playerNameBox.TabStop = false;
this.playerNameBox.Text = "Player Name";
//
// playerNameLabel
//
this.playerNameLabel.AutoSize = true;
this.playerNameLabel.Location = new System.Drawing.Point(46, 23);
this.playerNameLabel.Name = "playerNameLabel";
this.playerNameLabel.Size = new System.Drawing.Size(35, 13);
this.playerNameLabel.TabIndex = 401;
this.playerNameLabel.Text = "Name";
//
// playerNameInput
//
this.playerNameInput.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.playerNameInput.Location = new System.Drawing.Point(87, 20);
this.playerNameInput.Name = "playerNameInput";
this.playerNameInput.Size = new System.Drawing.Size(360, 20);
this.playerNameInput.TabIndex = 402;
this.playerNameInput.TextChanged += new System.EventHandler(this.playerNameInput_TextChanged);
//
// playerNameColoursBox
//
this.playerNameColoursBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.playerNameColoursBox.Controls.Add(this.playerNameColourButton);
this.playerNameColoursBox.Location = new System.Drawing.Point(49, 70);
this.playerNameColoursBox.Name = "playerNameColoursBox";
this.playerNameColoursBox.Size = new System.Drawing.Size(442, 279);
this.playerNameColoursBox.TabIndex = 450;
this.playerNameColoursBox.TabStop = false;
this.playerNameColoursBox.Text = "Colours";
//
// playerNameColourButton
//
this.playerNameColourButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.playerNameColourButton.Location = new System.Drawing.Point(18, 27);
this.playerNameColourButton.Name = "playerNameColourButton";
this.playerNameColourButton.Size = new System.Drawing.Size(32, 32);
this.playerNameColourButton.TabIndex = 451;
this.playerNameColourButton.Text = "A";
this.playerNameColourButton.TextAlign = System.Drawing.ContentAlignment.BottomRight;
this.playerNameColourButton.UseVisualStyleBackColor = true;
//
// playerNamePreview
//
this.playerNamePreview.Location = new System.Drawing.Point(87, 46);
this.playerNamePreview.Name = "playerNamePreview";
this.playerNamePreview.Size = new System.Drawing.Size(360, 18);
this.playerNamePreview.TabIndex = 410;
//
// gfxBox
//
this.gfxBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.gfxBox.Controls.Add(this.gfxResolutionLabel);
this.gfxBox.Controls.Add(this.gfxResolutionSelect);
this.gfxBox.Controls.Add(this.gfxFullscreen);
this.gfxBox.Location = new System.Drawing.Point(6, 372);
this.gfxBox.Name = "gfxBox";
this.gfxBox.Size = new System.Drawing.Size(540, 72);
this.gfxBox.TabIndex = 500;
this.gfxBox.TabStop = false;
this.gfxBox.Text = "Graphics";
//
// gfxFullscreen
//
this.gfxFullscreen.AutoSize = true;
this.gfxFullscreen.Location = new System.Drawing.Point(87, 46);
this.gfxFullscreen.Name = "gfxFullscreen";
this.gfxFullscreen.Size = new System.Drawing.Size(74, 17);
this.gfxFullscreen.TabIndex = 501;
this.gfxFullscreen.Text = "Fullscreen";
this.gfxFullscreen.UseVisualStyleBackColor = true;
this.gfxFullscreen.CheckedChanged += new System.EventHandler(this.gfxFullscreen_CheckedChanged);
//
// gfxResolutionLabel
//
this.gfxResolutionLabel.AutoSize = true;
this.gfxResolutionLabel.Location = new System.Drawing.Point(24, 22);
this.gfxResolutionLabel.Name = "gfxResolutionLabel";
this.gfxResolutionLabel.Size = new System.Drawing.Size(57, 13);
this.gfxResolutionLabel.TabIndex = 502;
this.gfxResolutionLabel.Text = "Resolution";
//
// gfxResolutionSelect
//
this.gfxResolutionSelect.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.gfxResolutionSelect.FormattingEnabled = true;
this.gfxResolutionSelect.Location = new System.Drawing.Point(87, 19);
this.gfxResolutionSelect.Name = "gfxResolutionSelect";
this.gfxResolutionSelect.Size = new System.Drawing.Size(360, 21);
this.gfxResolutionSelect.TabIndex = 503;
this.gfxResolutionSelect.SelectedIndexChanged += new System.EventHandler(this.gfxResolutionSelect_SelectedIndexChanged);
//
// MainWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(584, 561);
this.Controls.Add(this.versionLabel);
this.Controls.Add(this.mainTabs);
this.Controls.Add(this.launchGameButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.Name = "MainWindow";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "SoFii :: Soldier of Fortune 2 Tool for Flashii";
this.serverBox.ResumeLayout(false);
this.serverBox.PerformLayout();
this.gameBox.ResumeLayout(false);
this.gameBox.PerformLayout();
this.mainTabs.ResumeLayout(false);
this.locationsTabPage.ResumeLayout(false);
this.playerTabPage.ResumeLayout(false);
this.playerNameBox.ResumeLayout(false);
this.playerNameBox.PerformLayout();
this.playerNameColoursBox.ResumeLayout(false);
this.gfxBox.ResumeLayout(false);
this.gfxBox.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.GroupBox serverBox;
private System.Windows.Forms.Button launchGameButton;
private System.Windows.Forms.Label serverAddressLabel;
private System.Windows.Forms.TextBox serverAddressInput;
private System.Windows.Forms.Button serverResetButton;
private System.Windows.Forms.GroupBox gameBox;
private System.Windows.Forms.Label gameLocationLabel;
private System.Windows.Forms.TextBox gameLocationInput;
private System.Windows.Forms.Button gameBrowseButton;
private System.Windows.Forms.TabControl mainTabs;
private System.Windows.Forms.TabPage locationsTabPage;
private System.Windows.Forms.Label versionLabel;
private System.Windows.Forms.OpenFileDialog mainOpenFileDiag;
private System.Windows.Forms.CheckBox serverUseCustom;
private System.Windows.Forms.TabPage playerTabPage;
private System.Windows.Forms.GroupBox playerNameBox;
private System.Windows.Forms.Label playerNameLabel;
private System.Windows.Forms.TextBox playerNameInput;
private System.Windows.Forms.GroupBox playerNameColoursBox;
private System.Windows.Forms.Button playerNameColourButton;
private System.Windows.Forms.Panel playerNamePreview;
private System.Windows.Forms.GroupBox gfxBox;
private System.Windows.Forms.CheckBox gfxFullscreen;
private System.Windows.Forms.Label gfxResolutionLabel;
private System.Windows.Forms.ComboBox gfxResolutionSelect;
}
}

468
SoFii/MainWindow.cs Normal file
View file

@ -0,0 +1,468 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace SoFii {
public partial class MainWindow : Form {
private bool IsSizing = false;
private readonly ColourCharInfo[] Colours = EmbeddedResources.GetColourChars();
public MainWindow() {
InitializeComponent();
ResizeToFit(false);
Screen screen = Screen.FromControl(this);
Rectangle screenRect = screen.WorkingArea;
int left = (screenRect.Width / 2) - (Size.Width / 2);
int top = (screenRect.Height / 4) - (Size.Height / 2);
Location = new Point(screenRect.Y + left, screenRect.X + top);
versionLabel.Text = $"SoFii v{Program.GetSemVerString()}";
serverAddressInput.Text = Settings.ServerAddress;
if(Settings.UseDefaultServer) {
serverAddressInput.Enabled = false;
serverUseCustom.Checked = false;
LoadDefaultServer();
} else {
serverAddressInput.Enabled = true;
serverUseCustom.Checked = true;
}
CheckGameExists(Settings.GamePath);
DrawPlayerNameColourButtons();
playerNameInput.Text = Settings.PlayerName;
DrawPlayerNamePreview();
gfxResolutionSelect.Items.AddRange(new object[] {
string.Empty,
new SoF2VideoMode(3, "640x480"),
new SoF2VideoMode(4, "800x600"),
new SoF2VideoMode(5, "960x720"),
new SoF2VideoMode(6, "1024x768"),
new SoF2VideoMode(7, "1152x864"),
new SoF2VideoMode(8, "1280x1024"),
new SoF2VideoMode(9, "1600x1200"),
new SoF2VideoMode(10, "2048x1536"),
});
int gfxMode = Settings.GfxMode;
if(gfxMode >= 0)
gfxResolutionSelect.SelectedItem = gfxMode;
gfxFullscreen.Checked = Settings.GfxFullscreen;
}
private void DrawPlayerNameColourButtons() {
Control.ControlCollection container = playerNameColoursBox.Controls;
Button template = playerNameColourButton;
container.Remove(template);
int width = template.Size.Width;
int height = template.Size.Height;
int rowStart = template.Location.Y;
int colStart = template.Location.X;
int tabIndex = template.TabIndex;
int row = 0;
int col = 0;
List<int> handled = new List<int>();
foreach(ColourCharInfo cci in Colours) {
int argb = cci.Colour.ToArgb();
if(handled.Contains(argb))
continue;
handled.Add(argb);
Button colourButton = new Button() {
Text = cci.Char.ToString(),
TextAlign = ContentAlignment.BottomRight,
FlatStyle = FlatStyle.Flat,
UseVisualStyleBackColor = false,
BackColor = cci.Colour,
TabIndex = tabIndex,
Size = new Size(width, height),
Location = new Point(colStart + (col * width) + (col * 2), rowStart + (row * height) + (row * 2)),
};
colourButton.Click += (s, e) => {
int selPos = playerNameInput.SelectionStart;
playerNameInput.Text = playerNameInput.Text.Insert(selPos, $"^{cci.Char}");
playerNameInput.SelectionStart = selPos + 2;
};
container.Add(colourButton);
++tabIndex;
if(++col > 11) {
col = 0;
++row;
}
}
}
public void ResizeToFit(bool animate = true) {
int needHeight = 0;
Point scrollPos = AutoScrollPosition;
AutoScrollPosition = new Point(0, 0);
foreach(Control control in mainTabs.SelectedTab.Controls) {
int calcHeight = control.Size.Height + control.Location.Y;
if(calcHeight > needHeight)
needHeight = calcHeight;
}
needHeight += 83;
Screen screen = Screen.FromControl(this);
int maxHeight = (screen.WorkingArea.Height / 8) * 5;
if(needHeight > maxHeight)
needHeight = maxHeight;
void onFinish() {
AutoScrollPosition = scrollPos;
};
if(animate)
SetHeightAnimated(needHeight, onFinish);
else {
ClientSize = new Size(ClientSize.Width, needHeight);
onFinish();
}
}
public void SetHeightAnimated(int height, Action onFinish = null) {
if(IsSizing)
return;
IsSizing = true;
const int timeout = 1000 / 60;
double time = 0;
double period = timeout / 150d;
int currentHeight = ClientSize.Height;
int diffHeight = height - currentHeight;
Action setHeight = new Action(() => {
int newHeight = currentHeight + (int)Math.Ceiling(time * diffHeight);
ClientSize = new Size(ClientSize.Width, newHeight);
});
new Thread(() => {
Stopwatch sw = new Stopwatch();
try {
do {
sw.Reset();
sw.Start();
Invoke(setHeight);
time += period;
int delay = timeout - (int)sw.ElapsedMilliseconds;
if(delay > 1)
Thread.Sleep(delay);
} while(time < 1d);
} finally {
sw.Stop();
InvokeAction(() => {
ClientSize = new Size(ClientSize.Width, height);
onFinish?.Invoke();
Refresh();
});
IsSizing = false;
}
}) {
IsBackground = true,
Priority = ThreadPriority.AboveNormal,
}.Start();
}
public void InvokeAction(Action action) {
if(action is null)
throw new ArgumentNullException(nameof(action));
if(InvokeRequired)
Invoke(action);
else
action();
}
public bool CheckGameExists(string path) {
bool exists = !string.IsNullOrEmpty(path) && File.Exists(path);
gameLocationInput.Text = exists ? path : "Click the Browse... button and pick SoF2MP.exe";
launchGameButton.Enabled = exists;
return exists;
}
public void LaunchGame() {
string path = Settings.GamePath;
if(!CheckGameExists(path)) {
MessageBox.Show(this, "Game executable doesn't exist anymore. Please use the Browse... button to select it again.", "SoFii :: Error while launching game");
return;
}
string serverAddr = Settings.ServerAddress;
if(!Settings.UseDefaultServer && !serverAddressInput.Text.Equals(serverAddr))
Settings.ServerAddress = serverAddr = serverAddressInput.Text;
try {
Enabled = false;
WindowState = FormWindowState.Minimized;
StringBuilder args = new StringBuilder();
args.AppendFormat(@"+seta r_fullscreen ""{0}"" ", Settings.GfxFullscreen ? '1' : '0');
int vidMode = Settings.GfxMode;
if(vidMode >= 0)
args.AppendFormat(@"+seta r_mode ""{0}"" ", vidMode);
args.Append("+vid_restart ");
string playerName = Settings.PlayerName;
if(!string.IsNullOrEmpty(playerName))
args.AppendFormat(@"+seta name ""{0}"" ", playerName);
if(!string.IsNullOrEmpty(serverAddr))
args.AppendFormat(@"+connect ""{0}"" +seta server1 ""{0}""", serverAddr);
Process.Start(new ProcessStartInfo(path) {
Arguments = args.ToString(),
UseShellExecute = false,
WorkingDirectory = Path.GetDirectoryName(path),
}).WaitForExit();
} finally {
WindowState = FormWindowState.Normal;
Enabled = true;
}
}
public void LoadDefaultServer() {
if(!serverResetButton.Enabled)
return;
serverResetButton.Enabled = false;
bool serverAddressInputEnabled = serverAddressInput.Enabled;
serverAddressInput.Enabled = false;
serverAddressInput.Text = "Loading...";
new Thread(() => {
void reportError(string message, string caption = "Error while fetching default server") {
InvokeAction(() => {
MessageBox.Show(this, message, $"SoFii :: {caption}");
serverAddressInput.Text = string.Empty;
serverResetButton.Text = "Retry";
});
};
try {
using(DNSClient dns = new DNSClient()) {
DNSClient.Response resp = dns.QueryRecords(DNSClient.RecordType.TXT, Constants.SOFII_DOMAIN);
if(!resp.IsSuccess) {
reportError($"Could not resolve {Constants.SOFII_DOMAIN}.");
return;
}
string addr = string.Empty;
string port = string.Empty;
string ver = string.Empty;
StringBuilder messageBuilder = new StringBuilder();
foreach(DNSClient.Answer answer in resp.Answers) {
if(answer.Type != DNSClient.RecordType.TXT)
continue;
string text = answer.GetTXTData().Text.Trim();
if(text.StartsWith("msg=")) {
messageBuilder.AppendLine(text.Substring(4));
continue;
}
string[] textParts = text.Split(' ');
foreach(string textPart in textParts) {
if(textPart.StartsWith("addr=") && string.IsNullOrEmpty(addr))
addr = textPart.Substring(5);
else if(textPart.StartsWith("port=") && string.IsNullOrEmpty(port))
port = textPart.Substring(5);
else if(textPart.StartsWith("ver=") && string.IsNullOrEmpty(ver))
ver = textPart.Substring(4);
}
}
if(messageBuilder.Length > 0)
reportError(messageBuilder.ToString(), "Message from the server");
if(string.IsNullOrEmpty(addr)) {
if(messageBuilder.Length < 1)
reportError("Server did not respond with an address and did not supply additional information.");
return;
}
InvokeAction(() => {
serverResetButton.Text = "Reset";
string fullAddr = addr;
if(!string.IsNullOrEmpty(port))
fullAddr += $":{port}";
Settings.ServerAddress = fullAddr;
serverAddressInput.Text = fullAddr;
if(!string.IsNullOrEmpty(ver) && int.TryParse(ver, out int version)
&& Program.GetSemVerInt() < version && Settings.IgnoreNewVersion < version) {
DialogResult result = MessageBox.Show(
this,
"A newer version of SoFii is available that may include necessary and/or new features.\r\nCheck https://fii.moe/sofii for more information.\r\n\r\nPress OK to continue or Cancel if you don't want to be notified about this version again.\r\nThe Help button will open the information page in your default web browser.",
"SoFii :: New version notification",
MessageBoxButtons.OKCancel,
MessageBoxIcon.Information,
MessageBoxDefaultButton.Button1,
0,
Constants.INFO_URL
);
if(result == DialogResult.Cancel)
Settings.IgnoreNewVersion = version;
}
});
}
} catch(Exception ex) {
#if DEBUG
reportError(ex.ToString());
#else
reportError(ex.Message);
#endif
} finally {
InvokeAction(() => {
serverAddressInput.Enabled = serverAddressInputEnabled;
serverResetButton.Enabled = true;
});
}
}) { IsBackground = true }.Start();
}
private void serverResetButton_Click(object sender, EventArgs e) {
Settings.UseDefaultServer = true;
serverAddressInput.Enabled = false;
serverUseCustom.Checked = false;
LoadDefaultServer();
}
private void gameBrowseButton_Click(object sender, EventArgs e) {
if(mainOpenFileDiag.ShowDialog() == DialogResult.OK) {
string path = mainOpenFileDiag.FileName;
if(CheckGameExists(path))
Settings.GamePath = path;
}
}
private void launchGameButton_Click(object sender, EventArgs e) {
LaunchGame();
}
private void serverUseCustom_CheckedChanged(object sender, EventArgs e) {
bool useCustom = serverUseCustom.Checked;
bool useDefault = !useCustom;
if(Settings.UseDefaultServer != useDefault)
Settings.UseDefaultServer = useDefault;
serverAddressInput.Enabled = useCustom;
if(useDefault)
LoadDefaultServer();
}
private void mainTabs_SelectedIndexChanged(object sender, EventArgs e) {
ResizeToFit();
}
public void DrawPlayerNamePreview() {
string userName = playerNameInput.Text;
bool nextIsColourChar = false;
Color colour = SystemColors.ControlText;
Brush brush = new SolidBrush(colour);
Font font = SystemFonts.DefaultFont;
float offset = 0;
StringBuilder sb = new StringBuilder();
using(Graphics gfx = playerNamePreview.CreateGraphics()) {
gfx.Clear(SystemColors.ControlLightLight);
float spaceWidth = gfx.MeasureString(" ", font).Width + .1f;
void drawBuffer() {
if(sb.Length < 1)
return;
string str = sb.ToString();
sb.Length = 0;
gfx.DrawString(str, font, brush, offset, 0);
offset += gfx.MeasureString(str, font).Width - spaceWidth;
};
foreach(char chr in userName) {
if(nextIsColourChar) {
foreach(ColourCharInfo cci in Colours)
if(cci.Char == chr) {
if(cci.Colour != colour) {
colour = cci.Colour;
brush.Dispose();
brush = new SolidBrush(colour);
}
break;
}
nextIsColourChar = false;
continue;
}
if(chr == '^') {
nextIsColourChar = true;
drawBuffer();
continue;
}
sb.Append(chr);
}
drawBuffer();
}
brush.Dispose();
}
private void playerNameInput_TextChanged(object sender, EventArgs e) {
Settings.PlayerName = playerNameInput.Text;
DrawPlayerNamePreview();
}
private void gfxFullscreen_CheckedChanged(object sender, EventArgs e) {
Settings.GfxFullscreen = gfxFullscreen.Checked;
}
private void gfxResolutionSelect_SelectedIndexChanged(object sender, EventArgs e) {
if(gfxResolutionSelect.SelectedItem is SoF2VideoMode vidMode)
Settings.GfxMode = vidMode.Mode;
else
Settings.Remove(Settings.GFX_MODE);
}
}
}

2393
SoFii/MainWindow.resx Normal file

File diff suppressed because it is too large Load diff

90
SoFii/NameColours.txt Normal file
View file

@ -0,0 +1,90 @@
1 16718105
_ 16711680
$ 16711680
q 11507092
{ 12193024
T 13520444
C 15880997
: 15970943
b 14454844
z 11698482
k 13546113
@ 13009152
N 14454784
3 16776960
' 16776960
2 11125364
Y 8837120
E 7122728
K 11393921
g 8516137
? 8837198
n 5144370
j 4904960
| 8051530
G 4893989
S 4452905
B 3988521
P 65280
< 65280
J 9744277
v 907075
= 8838070
M 55418
p 2397548
V 8375986
9 3988661
a 62389
[ 3977109
] 2471573
X 2471597
> 2471597
5 65535
) 65535
f 7390414
I 5162204
} 2443859
y 954564
l 9487602
d 8367579
D 3296895
F 9328
x 3112
i 2443949
O 11910642
R 2702556
& 5000447
w 1
0 1
# 1
u 3941557
. 6111928
% 6111928
A 4915443
s 3276934
Z 11507164
U 9454578
/ 3211363
8 7995608
L 11696856
W 8782006
; 14107634
6 16711935
( 16711935
r 11948978
m 12395958
c 14107598
Q 15944637
e 14155910
H 9792641
\ 13454718
t 7995432
h 8781864
o 11496304
! 0
- 4144959
, 8355711
+ 12566463
7 16777215
* 16777215
" 16777215

39
SoFii/Program.cs Normal file
View file

@ -0,0 +1,39 @@
using System;
using System.Reflection;
using System.Windows.Forms;
namespace SoFii {
public static class Program {
public static readonly Assembly CurrentAssembly = Assembly.GetExecutingAssembly();
public static readonly AssemblyName CurrentAssemblyName = CurrentAssembly.GetName();
[STAThread]
public static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
int version = GetSemVerInt();
int lastVersion = Settings.LastVersion;
if(version < lastVersion) {
DialogResult result = MessageBox.Show("You're opening an older version of SoFii after already having used a newer version.\r\nThis could corrupt your settings. Are you sure you want to continue?", "SoFii :: Old version warning", MessageBoxButtons.YesNo);
if(result != DialogResult.Yes)
return;
Settings.LastVersion = version;
} else if(version > lastVersion)
Settings.LastVersion = version;
Application.Run(new MainWindow());
}
public static string GetSemVerString() {
Version version = CurrentAssemblyName.Version;
return $"{version.Major}.{version.Minor}.{version.Build}";
}
public static int GetSemVerInt() {
Version version = CurrentAssemblyName.Version;
return (version.Major << 20) | (version.Minor << 10) | version.Build;
}
}
}

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SoFii")]
[assembly: AssemblyDescription("Soldier of Fortune 2 Flashii assistant")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SoFii")]
[assembly: AssemblyCopyright("flash.moe 2023")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("6026586c-8730-428a-8e61-e75568ff7185")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// 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.0.0")]
[assembly: AssemblyFileVersion("1.0.0")]

73
SoFii/Properties/Resources.Designer.cs generated Normal file
View file

@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SoFii.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 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 {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SoFii.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 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 {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon logoproto {
get {
object obj = ResourceManager.GetObject("logoproto", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}
}

View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="logoproto" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\logoproto.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

43
SoFii/RNG.cs Normal file
View file

@ -0,0 +1,43 @@
using System;
namespace SoFii {
public static class RNG {
private static object Lock { get; } = new object();
private static Random NormalRandom { get; } = new Random();
public static int Next() {
lock(Lock)
return NormalRandom.Next();
}
public static int Next(int max) {
lock(Lock)
return NormalRandom.Next(max);
}
public static int Next(int min, int max) {
lock(Lock)
return NormalRandom.Next(min, max);
}
public static void NextBytes(byte[] buffer) {
lock(Lock)
NormalRandom.NextBytes(buffer);
}
public static void Shuffle<T>(T[] arr) {
if(arr is null)
throw new ArgumentNullException(nameof(arr));
lock(Lock) {
int n = arr.Length;
while(n > 1) {
int k = Next(n--);
T tmp = arr[n];
arr[n] = arr[k];
arr[k] = tmp;
}
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

170
SoFii/Settings.cs Normal file
View file

@ -0,0 +1,170 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace SoFii {
public static class Settings {
public const string LAST_VERSION = "LastVersion";
public const string GAME_PATH = "GamePath";
public const string USE_DEFAULT_SERVER = "UseDefaultServer";
public const string SERVER_ADDRESS = "ServerAddress";
public const string CUSTOM_DNS_SERVERS = "CustomDNSServers";
public const string PLAYER_NAME = "PlayerName";
public const string GFX_FULLSCREEN = "GfxFullscreen";
public const string GFX_MODE = "GfxMode";
public const string IGNORE_NEW_VERSION = "IgnoreNewVersion";
public static int LastVersion {
get => Get(LAST_VERSION, 0);
set => Set(LAST_VERSION, value);
}
public static string GamePath {
get => Get(GAME_PATH, string.Empty);
set => Set(GAME_PATH, value);
}
public static bool UseDefaultServer {
get => Get(USE_DEFAULT_SERVER, true);
set => Set(USE_DEFAULT_SERVER, value);
}
public static string ServerAddress {
get => Get(SERVER_ADDRESS, string.Empty);
set => Set(SERVER_ADDRESS, value);
}
public static string[] CustomDNSServers {
get => Get(CUSTOM_DNS_SERVERS, string.Empty).Split(' ');
set {
if(value == null || value.Length < 1)
Remove(CUSTOM_DNS_SERVERS);
else
Set(CUSTOM_DNS_SERVERS, string.Join(" ", value));
}
}
public static string PlayerName {
get => Get(PLAYER_NAME, "Speler");
set => Set(PLAYER_NAME, value);
}
public static bool GfxFullscreen {
get => Get(GFX_FULLSCREEN, false);
set => Set(GFX_FULLSCREEN, value);
}
public static int GfxMode {
get => Get(GFX_MODE, -1);
set => Set(GFX_MODE, value);
}
public static int IgnoreNewVersion {
get => Get(IGNORE_NEW_VERSION, 0);
set => Set(IGNORE_NEW_VERSION, value);
}
private const string ROOT = @"Software\flash.moe\SoFii";
private static RegistryKey GetRoot() {
RegistryKey root = Registry.CurrentUser.OpenSubKey(ROOT, true);
if(root == null)
root = Registry.CurrentUser.CreateSubKey(ROOT);
return root;
}
public static T Get<T>(string name, T fallback = default) {
try {
return (T)Convert.ChangeType(GetRoot().GetValue(name, fallback), typeof(T));
} catch {
return fallback;
}
}
public static string[] Get(string name, string[] fallback = null) {
if(!(GetRoot().GetValue(name, null) is byte[] buffer))
return fallback;
List<string> strings = new List<string>();
using(MemoryStream src = new MemoryStream(buffer))
using(MemoryStream ms = new MemoryStream()) {
int b;
for(; ; ) {
b = src.ReadByte();
if(b == -1)
break;
else if(b != 0)
ms.WriteByte((byte)b);
else {
strings.Add(Encoding.UTF8.GetString(ms.ToArray()));
ms.SetLength(0);
}
}
}
return strings.ToArray();
}
public static bool Has(string name) {
try {
GetRoot().GetValueKind(name);
return true;
} catch {
return false;
}
}
public static void Set(string name, object value) {
if(value == null) {
Remove(name);
return;
}
switch(value) {
case bool b:
value = b ? 1 : 0;
break;
}
GetRoot().SetValue(name, value);
}
public static void Set(string name, string[] values) {
if(values == null || values.Length < 1) {
Remove(name);
return;
}
using(MemoryStream ms = new MemoryStream()) {
foreach(string value in values) {
byte[] buffer = Encoding.UTF8.GetBytes(value);
ms.Write(buffer, 0, buffer.Length);
ms.WriteByte(0);
}
GetRoot().SetValue(name, ms.ToArray(), RegistryValueKind.Binary);
}
}
public static void SetDefault(string name, object value) {
if(!Has(name))
Set(name, value);
}
public static void SetDefault(string name, string[] value) {
if(!Has(name))
Set(name, value);
}
public static void Remove(string name) {
GetRoot().DeleteValue(name, false);
}
}
}

26
SoFii/SoF2VideoMode.cs Normal file
View file

@ -0,0 +1,26 @@
namespace SoFii {
public readonly struct SoF2VideoMode {
public int Mode { get; }
public string Resolution { get; }
public SoF2VideoMode(int mode, string res) {
Mode = mode;
Resolution = res;
}
public override string ToString() {
return Resolution;
}
public override bool Equals(object obj) {
if(obj is int num && num == Mode)
return true;
return base.Equals(obj);
}
public override int GetHashCode() {
return base.GetHashCode();
}
}
}

109
SoFii/SoFii.csproj Normal file
View file

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{6026586C-8730-428A-8E61-E75568FF7185}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>SoFii</RootNamespace>
<AssemblyName>SoFii</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\logoproto.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ColourCharInfo.cs" />
<Compile Include="Constants.cs" />
<Compile Include="Delegates.cs" />
<Compile Include="DNSClient.cs" />
<Compile Include="EmbeddedResources.cs" />
<Compile Include="MainWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MainWindow.Designer.cs">
<DependentUpon>MainWindow.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="RNG.cs" />
<Compile Include="Settings.cs" />
<Compile Include="SoF2VideoMode.cs" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MainWindow.resx">
<DependentUpon>MainWindow.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="Resources\logoproto.ico" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="DNSServers.txt" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="NameColours.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>