From e90a25d0ab7434a1ff557294275efdd7bc52478d Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Thu, 10 Sep 2015 19:00:44 -0400 Subject: [PATCH] Replace ini file reader with full .NET implementation so it can run on Mono. --- FFXIVClassic_Lobby_Server/ConfigConstants.cs | 19 +- .../FFXIVClassic_Lobby_Server.csproj | 2 +- FFXIVClassic_Lobby_Server/Server.cs | 2 +- FFXIVClassic_Lobby_Server/common/IniFile.cs | 63 --- FFXIVClassic_Lobby_Server/common/Log.cs | 10 +- .../common/STA_INIFile.cs | 533 ++++++++++++++++++ 6 files changed, 551 insertions(+), 78 deletions(-) delete mode 100644 FFXIVClassic_Lobby_Server/common/IniFile.cs create mode 100644 FFXIVClassic_Lobby_Server/common/STA_INIFile.cs diff --git a/FFXIVClassic_Lobby_Server/ConfigConstants.cs b/FFXIVClassic_Lobby_Server/ConfigConstants.cs index 2c543d4e..7968aa5e 100644 --- a/FFXIVClassic_Lobby_Server/ConfigConstants.cs +++ b/FFXIVClassic_Lobby_Server/ConfigConstants.cs @@ -1,4 +1,5 @@ using FFXIVClassic_Lobby_Server.common; +using STA.Settings; using System; using System.Collections.Generic; using System.IO; @@ -10,6 +11,7 @@ namespace FFXIVClassic_Lobby_Server { class ConfigConstants { + public static String OPTIONS_BINDIP; public static bool OPTIONS_TIMESTAMP = false; public static String DATABASE_HOST; @@ -29,16 +31,17 @@ namespace FFXIVClassic_Lobby_Server Console.ForegroundColor = ConsoleColor.Gray; return false; } - - IniFile ini = new IniFile("./config.ini"); - ConfigConstants.OPTIONS_TIMESTAMP = ini.IniReadValue("General", "showtimestamp").ToLower().Equals("true"); + INIFile configIni = new INIFile("./config.ini"); - ConfigConstants.DATABASE_HOST = ini.IniReadValue("Database", "host"); - ConfigConstants.DATABASE_PORT = ini.IniReadValue("Database", "port"); - ConfigConstants.DATABASE_NAME = ini.IniReadValue("Database", "database"); - ConfigConstants.DATABASE_USERNAME = ini.IniReadValue("Database", "username"); - ConfigConstants.DATABASE_PASSWORD = ini.IniReadValue("Database", "password"); + ConfigConstants.OPTIONS_BINDIP = configIni.GetValue("General", "server_ip", "127.0.0.1"); + ConfigConstants.OPTIONS_TIMESTAMP = configIni.GetValue("General", "showtimestamp", "true").ToLower().Equals("true"); + + ConfigConstants.DATABASE_HOST = configIni.GetValue("Database", "host", ""); + ConfigConstants.DATABASE_PORT = configIni.GetValue("Database", "port", ""); + ConfigConstants.DATABASE_NAME = configIni.GetValue("Database", "database", ""); + ConfigConstants.DATABASE_USERNAME = configIni.GetValue("Database", "username", ""); + ConfigConstants.DATABASE_PASSWORD = configIni.GetValue("Database", "password", ""); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("[OK]"); diff --git a/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj b/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj index b94227c1..203bf2dc 100644 --- a/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj +++ b/FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj @@ -56,12 +56,12 @@ + - diff --git a/FFXIVClassic_Lobby_Server/Server.cs b/FFXIVClassic_Lobby_Server/Server.cs index bccbc724..c3ec43f0 100644 --- a/FFXIVClassic_Lobby_Server/Server.cs +++ b/FFXIVClassic_Lobby_Server/Server.cs @@ -24,7 +24,7 @@ namespace FFXIVClassic_Lobby_Server #region Socket Handling public bool startServer() { - IPEndPoint serverEndPoint = new System.Net.IPEndPoint(IPAddress.Parse("127.0.0.1"), FFXIV_LOBBY_PORT); + IPEndPoint serverEndPoint = new System.Net.IPEndPoint(IPAddress.Parse(ConfigConstants.OPTIONS_BINDIP), FFXIV_LOBBY_PORT); try{ mServerSocket = new System.Net.Sockets.Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); diff --git a/FFXIVClassic_Lobby_Server/common/IniFile.cs b/FFXIVClassic_Lobby_Server/common/IniFile.cs deleted file mode 100644 index 65ff5b0e..00000000 --- a/FFXIVClassic_Lobby_Server/common/IniFile.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Runtime.InteropServices; -using System.Text; - -namespace FFXIVClassic_Lobby_Server.common -{ - /// - /// Create a New INI file to store or load data - /// - public class IniFile - { - public string path; - - [DllImport("kernel32")] - private static extern long WritePrivateProfileString(string section, - string key, string val, string filePath); - [DllImport("kernel32")] - private static extern int GetPrivateProfileString(string section, - string key, string def, StringBuilder retVal, - int size, string filePath); - [DllImport("kernel32")] - private static extern int GetPrivateProfileSectionNames(StringBuilder retVal, - int size, string filePath); - - /// - /// INIFile Constructor. - /// - /// - public IniFile(string INIPath) - { - path = INIPath; - } - /// - /// Write Data to the INI File - /// - /// - /// Section name - /// - /// Key Name - /// - /// Value Name - public void IniWriteValue(string Section, string Key, string Value) - { - WritePrivateProfileString(Section, Key, Value, this.path); - } - - /// - /// Read Data Value From the Ini File - /// - /// - /// - /// - /// - public string IniReadValue(string Section, string Key) - { - StringBuilder temp = new StringBuilder(255); - int i = GetPrivateProfileString(Section, Key, "", temp, - 255, this.path); - return temp.ToString(); - - } - - } -} \ No newline at end of file diff --git a/FFXIVClassic_Lobby_Server/common/Log.cs b/FFXIVClassic_Lobby_Server/common/Log.cs index 8c69312f..0fee864c 100644 --- a/FFXIVClassic_Lobby_Server/common/Log.cs +++ b/FFXIVClassic_Lobby_Server/common/Log.cs @@ -12,7 +12,7 @@ namespace FFXIVClassic_Lobby_Server.common { Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm")); Console.ForegroundColor = ConsoleColor.Red; - Console.Write("[ERROR]"); + Console.Write("[ERROR] "); Console.ForegroundColor = ConsoleColor.Gray ; Console.WriteLine(message); } @@ -21,7 +21,7 @@ namespace FFXIVClassic_Lobby_Server.common { Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm")); Console.ForegroundColor = ConsoleColor.Yellow; - Console.Write("[DEBUG]"); + Console.Write("[DEBUG] "); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(message); } @@ -30,7 +30,7 @@ namespace FFXIVClassic_Lobby_Server.common { Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm")); Console.ForegroundColor = ConsoleColor.Cyan; - Console.Write("[INFO]"); + Console.Write("[INFO] "); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(message); } @@ -39,7 +39,7 @@ namespace FFXIVClassic_Lobby_Server.common { Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm")); Console.ForegroundColor = ConsoleColor.Magenta; - Console.Write("[SQL]"); + Console.Write("[SQL] "); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(message); } @@ -48,7 +48,7 @@ namespace FFXIVClassic_Lobby_Server.common { Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm")); Console.ForegroundColor = ConsoleColor.DarkGreen; - Console.Write("[CONN]"); + Console.Write("[CONN] "); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine(message); } diff --git a/FFXIVClassic_Lobby_Server/common/STA_INIFile.cs b/FFXIVClassic_Lobby_Server/common/STA_INIFile.cs new file mode 100644 index 00000000..52f1f188 --- /dev/null +++ b/FFXIVClassic_Lobby_Server/common/STA_INIFile.cs @@ -0,0 +1,533 @@ +// ******************************* +// *** INIFile class V2.1 *** +// ******************************* +// *** (C)2009-2013 S.T.A. snc *** +// ******************************* +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace STA.Settings +{ + + internal class INIFile + { + +#region "Declarations" + + // *** Lock for thread-safe access to file and local cache *** + private object m_Lock = new object(); + + // *** File name *** + private string m_FileName = null; + internal string FileName + { + get + { + return m_FileName; + } + } + + // *** Lazy loading flag *** + private bool m_Lazy = false; + + // *** Automatic flushing flag *** + private bool m_AutoFlush = false; + + // *** Local cache *** + private Dictionary> m_Sections = new Dictionary>(); + private Dictionary> m_Modified = new Dictionary>(); + + // *** Local cache modified flag *** + private bool m_CacheModified = false; + +#endregion + +#region "Methods" + + // *** Constructor *** + public INIFile(string FileName) + { + Initialize(FileName, false, false); + } + + public INIFile(string FileName, bool Lazy, bool AutoFlush) + { + Initialize(FileName, Lazy, AutoFlush); + } + + // *** Initialization *** + private void Initialize(string FileName, bool Lazy, bool AutoFlush) + { + m_FileName = FileName; + m_Lazy = Lazy; + m_AutoFlush = AutoFlush; + if (!m_Lazy) Refresh(); + } + + // *** Parse section name *** + private string ParseSectionName(string Line) + { + if (!Line.StartsWith("[")) return null; + if (!Line.EndsWith("]")) return null; + if (Line.Length < 3) return null; + return Line.Substring(1, Line.Length - 2); + } + + // *** Parse key+value pair *** + private bool ParseKeyValuePair(string Line, ref string Key, ref string Value) + { + // *** Check for key+value pair *** + int i; + if ((i = Line.IndexOf('=')) <= 0) return false; + + int j = Line.Length - i - 1; + Key = Line.Substring(0, i).Trim(); + if (Key.Length <= 0) return false; + + Value = (j > 0) ? (Line.Substring(i + 1, j).Trim()) : (""); + return true; + } + + // *** Read file contents into local cache *** + internal void Refresh() + { + lock (m_Lock) + { + StreamReader sr = null; + try + { + // *** Clear local cache *** + m_Sections.Clear(); + m_Modified.Clear(); + + // *** Open the INI file *** + try + { + sr = new StreamReader(m_FileName); + } + catch (FileNotFoundException) + { + return; + } + + // *** Read up the file content *** + Dictionary CurrentSection = null; + string s; + string SectionName; + string Key = null; + string Value = null; + while ((s = sr.ReadLine()) != null) + { + s = s.Trim(); + + // *** Check for section names *** + SectionName = ParseSectionName(s); + if (SectionName != null) + { + // *** Only first occurrence of a section is loaded *** + if (m_Sections.ContainsKey(SectionName)) + { + CurrentSection = null; + } + else + { + CurrentSection = new Dictionary(); + m_Sections.Add(SectionName, CurrentSection); + } + } + else if (CurrentSection != null) + { + // *** Check for key+value pair *** + if (ParseKeyValuePair(s, ref Key, ref Value)) + { + // *** Only first occurrence of a key is loaded *** + if (!CurrentSection.ContainsKey(Key)) + { + CurrentSection.Add(Key, Value); + } + } + } + } + } + finally + { + // *** Cleanup: close file *** + if (sr != null) sr.Close(); + sr = null; + } + } + } + + // *** Flush local cache content *** + internal void Flush() + { + lock (m_Lock) + { + PerformFlush(); + } + } + + private void PerformFlush() + { + // *** If local cache was not modified, exit *** + if (!m_CacheModified) return; + m_CacheModified = false; + + // *** Check if original file exists *** + bool OriginalFileExists = File.Exists(m_FileName); + + // *** Get temporary file name *** + string TmpFileName = Path.ChangeExtension(m_FileName, "$n$"); + + // *** Copy content of original file to temporary file, replace modified values *** + StreamWriter sw = null; + + // *** Create the temporary file *** + sw = new StreamWriter(TmpFileName); + + try + { + Dictionary CurrentSection = null; + if (OriginalFileExists) + { + StreamReader sr = null; + try + { + // *** Open the original file *** + sr = new StreamReader(m_FileName); + + // *** Read the file original content, replace changes with local cache values *** + string s; + string SectionName; + string Key = null; + string Value = null; + bool Unmodified; + bool Reading = true; + while (Reading) + { + s = sr.ReadLine(); + Reading = (s != null); + + // *** Check for end of file *** + if (Reading) + { + Unmodified = true; + s = s.Trim(); + SectionName = ParseSectionName(s); + } + else + { + Unmodified = false; + SectionName = null; + } + + // *** Check for section names *** + if ((SectionName != null) || (!Reading)) + { + if (CurrentSection != null) + { + // *** Write all remaining modified values before leaving a section **** + if (CurrentSection.Count > 0) + { + foreach (string fkey in CurrentSection.Keys) + { + if (CurrentSection.TryGetValue(fkey, out Value)) + { + sw.Write(fkey); + sw.Write('='); + sw.WriteLine(Value); + } + } + sw.WriteLine(); + CurrentSection.Clear(); + } + } + + if (Reading) + { + // *** Check if current section is in local modified cache *** + if (!m_Modified.TryGetValue(SectionName, out CurrentSection)) + { + CurrentSection = null; + } + } + } + else if (CurrentSection != null) + { + // *** Check for key+value pair *** + if (ParseKeyValuePair(s, ref Key, ref Value)) + { + if (CurrentSection.TryGetValue(Key, out Value)) + { + // *** Write modified value to temporary file *** + Unmodified = false; + CurrentSection.Remove(Key); + + sw.Write(Key); + sw.Write('='); + sw.WriteLine(Value); + } + } + } + + // *** Write unmodified lines from the original file *** + if (Unmodified) + { + sw.WriteLine(s); + } + } + + // *** Close the original file *** + sr.Close(); + sr = null; + } + finally + { + // *** Cleanup: close files *** + if (sr != null) sr.Close(); + sr = null; + } + } + + // *** Cycle on all remaining modified values *** + foreach (KeyValuePair> SectionPair in m_Modified) + { + CurrentSection = SectionPair.Value; + if (CurrentSection.Count > 0) + { + sw.WriteLine(); + + // *** Write the section name *** + sw.Write('['); + sw.Write(SectionPair.Key); + sw.WriteLine(']'); + + // *** Cycle on all key+value pairs in the section *** + foreach (KeyValuePair ValuePair in CurrentSection) + { + // *** Write the key+value pair *** + sw.Write(ValuePair.Key); + sw.Write('='); + sw.WriteLine(ValuePair.Value); + } + CurrentSection.Clear(); + } + } + m_Modified.Clear(); + + // *** Close the temporary file *** + sw.Close(); + sw = null; + + // *** Rename the temporary file *** + File.Copy(TmpFileName, m_FileName, true); + + // *** Delete the temporary file *** + File.Delete(TmpFileName); + } + finally + { + // *** Cleanup: close files *** + if (sw != null) sw.Close(); + sw = null; + } + } + + // *** Read a value from local cache *** + internal string GetValue(string SectionName, string Key, string DefaultValue) + { + // *** Lazy loading *** + if (m_Lazy) + { + m_Lazy = false; + Refresh(); + } + + lock (m_Lock) + { + // *** Check if the section exists *** + Dictionary Section; + if (!m_Sections.TryGetValue(SectionName, out Section)) return DefaultValue; + + // *** Check if the key exists *** + string Value; + if (!Section.TryGetValue(Key, out Value)) return DefaultValue; + + // *** Return the found value *** + return Value; + } + } + + // *** Insert or modify a value in local cache *** + internal void SetValue(string SectionName, string Key, string Value) + { + // *** Lazy loading *** + if (m_Lazy) + { + m_Lazy = false; + Refresh(); + } + + lock (m_Lock) + { + // *** Flag local cache modification *** + m_CacheModified = true; + + // *** Check if the section exists *** + Dictionary Section; + if (!m_Sections.TryGetValue(SectionName, out Section)) + { + // *** If it doesn't, add it *** + Section = new Dictionary(); + m_Sections.Add(SectionName,Section); + } + + // *** Modify the value *** + if (Section.ContainsKey(Key)) Section.Remove(Key); + Section.Add(Key, Value); + + // *** Add the modified value to local modified values cache *** + if (!m_Modified.TryGetValue(SectionName, out Section)) + { + Section = new Dictionary(); + m_Modified.Add(SectionName, Section); + } + + if (Section.ContainsKey(Key)) Section.Remove(Key); + Section.Add(Key, Value); + + // *** Automatic flushing : immediately write any modification to the file *** + if (m_AutoFlush) PerformFlush(); + } + } + + // *** Encode byte array *** + private string EncodeByteArray(byte[] Value) + { + if (Value == null) return null; + + StringBuilder sb = new StringBuilder(); + foreach (byte b in Value) + { + string hex = Convert.ToString(b, 16); + int l = hex.Length; + if (l > 2) + { + sb.Append(hex.Substring(l - 2, 2)); + } + else + { + if (l < 2) sb.Append("0"); + sb.Append(hex); + } + } + return sb.ToString(); + } + + // *** Decode byte array *** + private byte[] DecodeByteArray(string Value) + { + if (Value == null) return null; + + int l = Value.Length; + if (l < 2) return new byte[] { }; + + l /= 2; + byte[] Result = new byte[l]; + for (int i = 0; i < l; i++) Result[i] = Convert.ToByte(Value.Substring(i * 2, 2), 16); + return Result; + } + + // *** Getters for various types *** + internal bool GetValue(string SectionName, string Key, bool DefaultValue) + { + string StringValue = GetValue(SectionName, Key, DefaultValue.ToString(System.Globalization.CultureInfo.InvariantCulture)); + int Value; + if (int.TryParse(StringValue, out Value)) return (Value != 0); + return DefaultValue; + } + + internal int GetValue(string SectionName, string Key, int DefaultValue) + { + string StringValue = GetValue(SectionName, Key, DefaultValue.ToString(CultureInfo.InvariantCulture)); + int Value; + if (int.TryParse(StringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out Value)) return Value; + return DefaultValue; + } + + internal long GetValue(string SectionName, string Key, long DefaultValue) + { + string StringValue = GetValue(SectionName, Key, DefaultValue.ToString(CultureInfo.InvariantCulture)); + long Value; + if (long.TryParse(StringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out Value)) return Value; + return DefaultValue; + } + + internal double GetValue(string SectionName, string Key, double DefaultValue) + { + string StringValue = GetValue(SectionName, Key, DefaultValue.ToString(CultureInfo.InvariantCulture)); + double Value; + if (double.TryParse(StringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out Value)) return Value; + return DefaultValue; + } + + internal byte[] GetValue(string SectionName, string Key, byte[] DefaultValue) + { + string StringValue = GetValue(SectionName, Key, EncodeByteArray(DefaultValue)); + try + { + return DecodeByteArray(StringValue); + } + catch (FormatException) + { + return DefaultValue; + } + } + + internal DateTime GetValue(string SectionName, string Key, DateTime DefaultValue) + { + string StringValue = GetValue(SectionName, Key, DefaultValue.ToString(CultureInfo.InvariantCulture)); + DateTime Value; + if (DateTime.TryParse(StringValue, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AssumeLocal, out Value)) return Value; + return DefaultValue; + } + + // *** Setters for various types *** + internal void SetValue(string SectionName, string Key, bool Value) + { + SetValue(SectionName, Key, (Value) ? ("1") : ("0")); + } + + internal void SetValue(string SectionName, string Key, int Value) + { + SetValue(SectionName, Key, Value.ToString(CultureInfo.InvariantCulture)); + } + + internal void SetValue(string SectionName, string Key, long Value) + { + SetValue(SectionName, Key, Value.ToString(CultureInfo.InvariantCulture)); + } + + internal void SetValue(string SectionName, string Key, double Value) + { + SetValue(SectionName, Key, Value.ToString(CultureInfo.InvariantCulture)); + } + + internal void SetValue(string SectionName, string Key, byte[] Value) + { + SetValue(SectionName, Key, EncodeByteArray(Value)); + } + + internal void SetValue(string SectionName, string Key, DateTime Value) + { + SetValue(SectionName, Key, Value.ToString(CultureInfo.InvariantCulture)); + } + +#endregion + + } + +}