mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-07-04 13:47:04 -04:00
Removed NioTSO client and server
- NioTSO client isn't needed because we're using RayLib - Added FreeSO's API server to handle most backend operations
This commit is contained in:
parent
f12ba1502b
commit
22191ce648
591 changed files with 53264 additions and 3362 deletions
41
server/FSO.Server.Debug/PacketAnalyzer/ByteCountPacketAnalyzer.cs
Executable file
41
server/FSO.Server.Debug/PacketAnalyzer/ByteCountPacketAnalyzer.cs
Executable file
|
@ -0,0 +1,41 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace FSO.Server.Debug.PacketAnalyzer
|
||||
{
|
||||
public class ByteCountPacketAnalyzer : IPacketAnalyzer
|
||||
{
|
||||
#region IPacketAnalyzer Members
|
||||
|
||||
public List<PacketAnalyzerResult> Analyze(byte[] data)
|
||||
{
|
||||
var result = new List<PacketAnalyzerResult>();
|
||||
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
if (i + 4 < data.Length)
|
||||
{
|
||||
byte len1 = data[i];
|
||||
byte len2 = data[i + 1];
|
||||
byte len3 = data[i + 2];
|
||||
byte len4 = data[i + 3];
|
||||
|
||||
long len = len1 << 24 | len2 << 16 | len3 << 8 | len4;
|
||||
|
||||
if (len == data.Length - (i + 4))
|
||||
{
|
||||
result.Add(new PacketAnalyzerResult
|
||||
{
|
||||
Offset = i,
|
||||
Length = 4,
|
||||
Description = "byte-count(" + len + ")"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
226
server/FSO.Server.Debug/PacketAnalyzer/ConstantsPacketAnalyzer.cs
Executable file
226
server/FSO.Server.Debug/PacketAnalyzer/ConstantsPacketAnalyzer.cs
Executable file
|
@ -0,0 +1,226 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FSO.Server.Protocol.Voltron.Model;
|
||||
using FSO.Server.Protocol.Voltron;
|
||||
using FSO.Common.DatabaseService.Model;
|
||||
|
||||
namespace FSO.Server.Debug.PacketAnalyzer
|
||||
{
|
||||
public class ConstantsPacketAnalyzer : IPacketAnalyzer
|
||||
{
|
||||
private static List<Constant> Constants = new List<Constant>();
|
||||
|
||||
static ConstantsPacketAnalyzer()
|
||||
{
|
||||
/*var voltronTypes = Enum.GetNames(typeof(VoltronPacketType));
|
||||
foreach (var name in voltronTypes)
|
||||
{
|
||||
var enumValue = (VoltronPacketType)Enum.Parse(typeof(VoltronPacketType), name);
|
||||
if (enumValue == VoltronPacketType.Unknown) { continue; }
|
||||
|
||||
Constants.Add(new Constant {
|
||||
Type = ConstantType.USHORT,
|
||||
Description = "VoltronPacket." + name,
|
||||
Value = VoltronPacketTypeUtils.GetPacketCode(enumValue)
|
||||
});
|
||||
}*/
|
||||
|
||||
var dbRequestTypes = Enum.GetNames(typeof(DBRequestType));
|
||||
foreach (var name in dbRequestTypes)
|
||||
{
|
||||
var enumValue = (DBRequestType)Enum.Parse(typeof(DBRequestType), name);
|
||||
if (enumValue == DBRequestType.Unknown) { continue; }
|
||||
|
||||
Constants.Add(new Constant
|
||||
{
|
||||
Type = ConstantType.UINT,
|
||||
Description = "DBRequestType." + name,
|
||||
Value = DBRequestTypeUtils.GetRequestID(enumValue)
|
||||
});
|
||||
|
||||
if (enumValue == DBRequestType.UpdatePreferedLanguageByID)
|
||||
{
|
||||
var val = DBRequestTypeUtils.GetRequestID(enumValue);
|
||||
}
|
||||
}
|
||||
|
||||
var dbResponseTypes = Enum.GetNames(typeof(DBResponseType));
|
||||
foreach (var name in dbResponseTypes)
|
||||
{
|
||||
var enumValue = (DBResponseType)Enum.Parse(typeof(DBResponseType), name);
|
||||
if (enumValue == DBResponseType.Unknown)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Constants.Add(new Constant
|
||||
{
|
||||
Type = ConstantType.UINT,
|
||||
Description = "DBResponseType." + name,
|
||||
Value = DBResponseTypeUtils.GetResponseID(enumValue)
|
||||
});
|
||||
}
|
||||
|
||||
var magicNumberTypes = Enum.GetNames(typeof(MagicNumberEnum));
|
||||
foreach (var name in magicNumberTypes)
|
||||
{
|
||||
var enumValue = (MagicNumberEnum)Enum.Parse(typeof(MagicNumberEnum), name);
|
||||
var intVal = MagicNumberEnumUtils.ToInt(enumValue);
|
||||
var floatVal = MagicNumberEnumUtils.ToFloat(enumValue);
|
||||
|
||||
Constants.Add(new Constant
|
||||
{
|
||||
Type = ConstantType.UINT,
|
||||
Description = "MagicNumberEnum." + name + "( " + intVal + ", " + floatVal + ")",
|
||||
Value = MagicNumberEnumUtils.ToID(enumValue)
|
||||
});
|
||||
}
|
||||
|
||||
var voltronTypes = Enum.GetNames(typeof(VoltronPacketType));
|
||||
foreach (var name in voltronTypes)
|
||||
{
|
||||
var enumValue = (VoltronPacketType)Enum.Parse(typeof(VoltronPacketType), name);
|
||||
if (enumValue == VoltronPacketType.Unknown)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var intVal = VoltronPacketTypeUtils.GetPacketCode(enumValue);
|
||||
|
||||
Constants.Add(new Constant
|
||||
{
|
||||
Type = ConstantType.USHORT,
|
||||
Description = "VoltronPacketType." + name,
|
||||
Value = intVal
|
||||
});
|
||||
}
|
||||
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x09736027, Description = "cTSOTopicUpdateMessage" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x0A2C6585, Description = "cTSODataTransportBuffer" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x496A78BC, Description = "cTSODataDefinitionContainer" });
|
||||
|
||||
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x097608AB, Description = "cTSOValueVector < unsigned char>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x097608B3, Description = "cTSOValueVector < signed char>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x097608B6, Description = "cTSOValueVector < unsigned short>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x29739B14, Description = "cTSOTopic" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x2A404946, Description = "cTSOTopicUpdateErrorMessage" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x496A78BC, Description = "cTSODataDefinitionContainer" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x696D1183, Description = "cTSOValue < bool >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x696D1189, Description = "cTSOValue < unsigned long>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x69D3E3DB, Description = "cTSOValue < unsigned __int64 >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x896D1196, Description = "cTSOValue < long >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x896D11A2, Description = "cTSOValueMap <class cRZAutoRefCount<class cITSOProperty> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x896D1688, Description = "cTSOValue<class cRZAutoRefCount<class cIGZString> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x89738492, Description = "cTSOValueBVector" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x89738496, Description = "cTSOValueVector<unsigned long>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x8973849A, Description = "cTSOValueVector<long>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x8973849E, Description = "cTSOValueVector <class cRZAutoRefCount<class cIGZString> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x89739A79, Description = "cTSOProperty" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x89D3E3EF, Description = "cTSOValue<__int64>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0x89D3E40E, Description = "cTSOValueMap<unsigned __int64>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA96E38A0, Description = "cTSOValueMap<unsigned long>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA96E38A8, Description = "cTSOValueMap<long>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA96E38AC, Description = "cTSOValueMap <class cRZAutoRefCount<class cIGZString> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA96E7E5B, Description = "cTSOValue<class cRZAutoRefCount<class cITSOProperty> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA97353EE, Description = "cTSODataService" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA97384A3, Description = "cTSOValueVector<class cRZAutoRefCount<class cITSOProperty> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA975FA6E, Description = "cTSODataServiceClient" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA99AF3A8, Description = "cTSOValueMap<class cRZAutoRefCount<class cIGZUnknown> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA99AF3AC, Description = "cTSOValueVector <class cRZAutoRefCount<class cIGZUnknown> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA99AF3B7, Description = "cTSOValue<class cRZAutoRefCount<class cIGZUnknown> >" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA9D3E412, Description = "cTSOValueMap<__int64>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA9D3E428, Description = "cTSOValueVector <unsigned __int64>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xA9D3E42D, Description = "cTSOValueVector<__int64>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xC976087C, Description = "cTSOValue<unsigned char>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xC97757F5, Description = "cTSOValueMap<bool>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xE976088A, Description = "cTSOValue<signed char>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xE9760891, Description = "cTSOValue<unsigned short>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xE9760897, Description = "cTSOValue<short>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xE976089F, Description = "cTSOValueMap<unsigned char>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xE97608A2, Description = "cTSOValueMap<signed char>" });
|
||||
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xE97608A5, Description = "cTSOValueMap <unsigned short>" });
|
||||
Constants.Add(new Constant { Type = ConstantType.UINT, Value = 0xE97608A8, Description = "cTSOValueMap<short>" });
|
||||
}
|
||||
|
||||
|
||||
#region IPacketAnalyzer Members
|
||||
public List<PacketAnalyzerResult> Analyze(byte[] data)
|
||||
{
|
||||
var result = new List<PacketAnalyzerResult>();
|
||||
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
foreach (var constant in GetConstants())
|
||||
{
|
||||
int len = 0;
|
||||
object value = null;
|
||||
switch (constant.Type)
|
||||
{
|
||||
case ConstantType.UINT:
|
||||
if (i + 4 >= data.Length) { continue; }
|
||||
byte len1 = data[i];
|
||||
byte len2 = data[i + 1];
|
||||
byte len3 = data[i + 2];
|
||||
byte len4 = data[i + 3];
|
||||
value = (uint)(len1 << 24 | len2 << 16 | len3 << 8 | len4);
|
||||
len = 4;
|
||||
break;
|
||||
case ConstantType.USHORT:
|
||||
if (i + 2 >= data.Length) { continue; }
|
||||
byte short1 = data[i];
|
||||
byte short2 = data[i + 1];
|
||||
value = (ushort)(short1 << 8 | short2);
|
||||
len = 2;
|
||||
break;
|
||||
case ConstantType.ULONG:
|
||||
if (i + 8 >= data.Length) { continue; }
|
||||
byte long1 = data[i];
|
||||
byte long2 = data[i + 1];
|
||||
byte long3 = data[i + 2];
|
||||
byte long4 = data[i + 3];
|
||||
byte long5 = data[i + 4];
|
||||
byte long6 = data[i + 5];
|
||||
byte long7 = data[i + 6];
|
||||
byte long8 = data[i + 7];
|
||||
|
||||
value = (ulong)(long1 << 54 | long2 << 48 | long3 << 40 | long4 << 32 | long5 << 24 | long6 << 16 | long7 << 8 | long8);
|
||||
len = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (value.ToString() == constant.Value.ToString())
|
||||
{
|
||||
result.Add(new PacketAnalyzerResult {
|
||||
Offset = i,
|
||||
Length = len,
|
||||
Description = constant.Description
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public virtual List<Constant> GetConstants()
|
||||
{
|
||||
return Constants;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Constant
|
||||
{
|
||||
public ConstantType Type;
|
||||
public object Value;
|
||||
public string Description;
|
||||
}
|
||||
|
||||
public enum ConstantType
|
||||
{
|
||||
USHORT,
|
||||
UINT,
|
||||
ULONG
|
||||
}
|
||||
}
|
58
server/FSO.Server.Debug/PacketAnalyzer/ContentPacketAnalyzer.cs
Executable file
58
server/FSO.Server.Debug/PacketAnalyzer/ContentPacketAnalyzer.cs
Executable file
|
@ -0,0 +1,58 @@
|
|||
using FSO.Content.Framework;
|
||||
using FSO.Vitaboy;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FSO.Server.Debug.PacketAnalyzer
|
||||
{
|
||||
public class ContentPacketAnalyzer : ConstantsPacketAnalyzer
|
||||
{
|
||||
private List<Constant> Constants = new List<Constant>();
|
||||
|
||||
public ContentPacketAnalyzer()
|
||||
{
|
||||
var content = Content.Content.Get();
|
||||
|
||||
/** Avatar Collections **/
|
||||
foreach(var collection in content.AvatarCollections.List())
|
||||
{
|
||||
var items = collection.Get();
|
||||
var collectionCast = (Far3ProviderEntry<Collection>)collection;
|
||||
|
||||
foreach(var item in items)
|
||||
{
|
||||
Constants.Add(new Constant {
|
||||
Type = ConstantType.ULONG,
|
||||
Value = item.PurchasableOutfitId,
|
||||
Description = collectionCast.FarEntry.Filename + "." + item.Index
|
||||
});
|
||||
|
||||
/**Constants.Add(new Constant
|
||||
{
|
||||
Type = ConstantType.UINT,
|
||||
Value = item.FileID,
|
||||
Description = collectionCast.FarEntry.Filename + "." + item.Index
|
||||
});**/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//TSODataDefinition file
|
||||
var dataDef = content.DataDefinition;
|
||||
|
||||
foreach (var str in dataDef.Strings)
|
||||
{
|
||||
Constants.Add(new Constant
|
||||
{
|
||||
Type = ConstantType.UINT,
|
||||
Description = "TSOData_datadefinition(" + str.Value + ")",
|
||||
Value = str.ID
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override List<Constant> GetConstants()
|
||||
{
|
||||
return Constants;
|
||||
}
|
||||
}
|
||||
}
|
22
server/FSO.Server.Debug/PacketAnalyzer/IPacketAnalyzer.cs
Executable file
22
server/FSO.Server.Debug/PacketAnalyzer/IPacketAnalyzer.cs
Executable file
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace FSO.Server.Debug.PacketAnalyzer
|
||||
{
|
||||
public interface IPacketAnalyzer
|
||||
{
|
||||
List<PacketAnalyzerResult> Analyze(byte[] data);
|
||||
}
|
||||
|
||||
public class PacketAnalyzerResult
|
||||
{
|
||||
public int Offset;
|
||||
public int Length;
|
||||
|
||||
public string Description;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Description;
|
||||
}
|
||||
}
|
||||
}
|
66
server/FSO.Server.Debug/PacketAnalyzer/PascalStringPacketAnalyzer.cs
Executable file
66
server/FSO.Server.Debug/PacketAnalyzer/PascalStringPacketAnalyzer.cs
Executable file
|
@ -0,0 +1,66 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FSO.Server.Debug.PacketAnalyzer
|
||||
{
|
||||
public class PascalStringPacketAnalyzer : IPacketAnalyzer
|
||||
{
|
||||
#region IPacketAnalyzer Members
|
||||
|
||||
public List<PacketAnalyzerResult> Analyze(byte[] data)
|
||||
{
|
||||
var result = new List<PacketAnalyzerResult>();
|
||||
|
||||
/**
|
||||
* We are looking for a uint32 with the msb flipped for length followed by that many chars within a reasonable ascii range
|
||||
*/
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var stringValue = GetString(data, i);
|
||||
if (stringValue != null)
|
||||
{
|
||||
result.Add(new PacketAnalyzerResult {
|
||||
Offset = i,
|
||||
Length = stringValue.Length + 4,
|
||||
Description = "pascal-string(" + stringValue + ")"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string GetString(byte[] data, int index)
|
||||
{
|
||||
if (index + 4 <= data.Length)
|
||||
{
|
||||
byte len1 = data[index];
|
||||
byte len2 = data[index + 1];
|
||||
byte len3 = data[index + 2];
|
||||
byte len4 = data[index + 3];
|
||||
|
||||
//Is msb set?
|
||||
if ((len1 >> 7) != 1) { return null; }
|
||||
len1 &= 0x7F;
|
||||
|
||||
long len = len1 << 24 | len2 << 16 | len3 << 8 | len4;
|
||||
if (len < 0) { return null; }
|
||||
|
||||
if (index + 4 + len <= data.Length)
|
||||
{
|
||||
/** Could be! **/
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
byte charValue = data[index + 4 + i];
|
||||
str.Append((char)charValue);
|
||||
}
|
||||
return str.ToString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
97
server/FSO.Server.Debug/PacketAnalyzer/VariableLengthStringAnalyzer.cs
Executable file
97
server/FSO.Server.Debug/PacketAnalyzer/VariableLengthStringAnalyzer.cs
Executable file
|
@ -0,0 +1,97 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FSO.Server.Debug.PacketAnalyzer
|
||||
{
|
||||
public class VariableLengthStringAnalyzer : IPacketAnalyzer
|
||||
{
|
||||
public List<PacketAnalyzerResult> Analyze(byte[] data)
|
||||
{
|
||||
var result = new List<PacketAnalyzerResult>();
|
||||
|
||||
/**
|
||||
* We are looking for a uint32 with the msb flipped for length followed by that many chars within a reasonable ascii range
|
||||
*/
|
||||
for (var i = 0; i < data.Length; i++)
|
||||
{
|
||||
var stringValue = GetString(data, i);
|
||||
if (stringValue != null)
|
||||
{
|
||||
var offset = 1;
|
||||
if(stringValue.Length > 128)
|
||||
{
|
||||
offset = 2;
|
||||
}
|
||||
|
||||
result.Add(new PacketAnalyzerResult
|
||||
{
|
||||
Offset = i,
|
||||
Length = stringValue.Length + offset,
|
||||
Description = "var-string(" + stringValue + ")"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string GetString(byte[] data, int index)
|
||||
{
|
||||
byte lengthByte = 0;
|
||||
uint length = 0;
|
||||
int shift = 0;
|
||||
byte lengthBytes = 0;
|
||||
|
||||
do
|
||||
{
|
||||
lengthByte = data[index + lengthBytes];
|
||||
length |= (uint)((lengthByte & (uint)0x7F) << shift);
|
||||
shift += 7;
|
||||
lengthBytes++;
|
||||
} while (
|
||||
(lengthByte >> 7) == 1 &&
|
||||
index + lengthBytes < data.Length
|
||||
);
|
||||
|
||||
if (length > 600 || length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if((index + lengthBytes + length) <= data.Length)
|
||||
{
|
||||
/** Could be! **/
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
byte charValue = data[index + lengthBytes + i];
|
||||
|
||||
|
||||
if(charValue == 0x09)
|
||||
{
|
||||
//Tab
|
||||
}
|
||||
else if(charValue == 0x0A)
|
||||
{
|
||||
//Line feed
|
||||
}else if(charValue == 0x0D)
|
||||
{
|
||||
//CR
|
||||
}else if(charValue >= 0x20 && charValue <= 0x7E)
|
||||
{
|
||||
//a-z, special chars, numbers
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
str.Append((char)charValue);
|
||||
}
|
||||
return str.ToString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue