Initial commit of the FFXIV 1.0 lobby server.

This commit is contained in:
Filip Maj 2015-08-26 13:38:58 -04:00
commit c1e214175f
48 changed files with 55780 additions and 0 deletions

View file

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using FFXIVClassic_Lobby_Server.packets;
using System.Diagnostics;
using FFXIVClassic_Lobby_Server.common;
using System.Collections.Concurrent;
using System.IO;
using Cyotek.Collections.Generic;
using System.Net;
namespace FFXIVClassic_Lobby_Server
{
class ClientConnection
{
//Connection stuff
public Blowfish blowfish;
public Socket socket;
public byte[] buffer = new byte[0xffff];
public CircularBuffer<byte> incomingStream = new CircularBuffer<byte>(1024);
public BlockingCollection<BasePacket> sendPacketQueue = new BlockingCollection<BasePacket>(100);
//Instance Stuff
public uint currentSession;
public uint currentAccount;
public void processIncoming(int bytesIn)
{
if (bytesIn == 0)
return;
incomingStream.Put(buffer, 0, bytesIn);
}
public void queuePacket(BasePacket packet)
{
sendPacketQueue.Add(packet);
}
public void flushQueuedSendPackets()
{
while (sendPacketQueue.Count > 0)
{
BasePacket packet = sendPacketQueue.Take();
byte[] packetBytes = packet.getPacketBytes();
byte[] buffer = new byte[0xffff];
Array.Copy(packetBytes, buffer, packetBytes.Length);
try {
socket.Send(packetBytes);
}
catch(Exception e)
{ Debug.WriteLine("Weird case, socket was d/ced: {0}", e); }
}
}
public String getAddress()
{
return String.Format("{0}:{1}", (socket.RemoteEndPoint as IPEndPoint).Address, (socket.RemoteEndPoint as IPEndPoint).Port);
}
public void disconnect()
{
socket.Disconnect(false);
}
}
}

View file

@ -0,0 +1,50 @@
using FFXIVClassic_Lobby_Server.common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server
{
class ConfigConstants
{
public static bool OPTIONS_TIMESTAMP = false;
public static String DATABASE_HOST;
public static String DATABASE_PORT;
public static String DATABASE_NAME;
public static String DATABASE_USERNAME;
public static String DATABASE_PASSWORD;
public static bool load()
{
Console.Write("Loading config.ini file... ");
if (!File.Exists("./config.ini"))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[FILE NOT FOUND]");
Console.ForegroundColor = ConsoleColor.Gray;
return false;
}
IniFile ini = new IniFile("./config.ini");
ConfigConstants.OPTIONS_TIMESTAMP = ini.IniReadValue("General", "showtimestamp").ToLower().Equals("true");
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");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("[OK]");
Console.ForegroundColor = ConsoleColor.Gray;
return true;
}
}
}

View file

@ -0,0 +1,238 @@
using FFXIVClassic_Lobby_Server.dataobjects;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server
{
//charState: 0 - Reserved, 1 - Deleted, 2 - Inactive, 3 - Active
class Database
{
public static void reserveCharacter(int accountId, int slot, int serverId, String name)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "INSERT INTO ffxiv_characters2(accountId, slot, serverId, name, charState) VALUES(@accountId, @slot, @serverId, @name, 0)";
cmd.Prepare();
cmd.Parameters.AddWithValue("@accountId", accountId);
cmd.Parameters.AddWithValue("@slot", slot);
cmd.Parameters.AddWithValue("@serverId", serverId);
cmd.Parameters.AddWithValue("@name", name);
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
}
finally
{
conn.Close();
}
}
}
public static void makeCharacter(int accountId, String name, Character charaInfo)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE ffxiv_characters2 SET data=@encodedInfo WHERE accountId=@accountId AND name=@name";
cmd.Prepare();
cmd.Parameters.AddWithValue("@accountId", accountId);
cmd.Parameters.AddWithValue("@name", name);
cmd.Parameters.AddWithValue("@encodedInfo", JsonConvert.SerializeObject(charaInfo));
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
}
finally
{
conn.Close();
}
}
}
public static void renameCharacter(uint characterId, String newName)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE ffxiv_characters2 SET name=@name WHERE id=@cid";
cmd.Prepare();
cmd.Parameters.AddWithValue("@cid", characterId);
cmd.Parameters.AddWithValue("@name", newName);
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
}
finally
{
conn.Close();
}
}
}
public static void deleteCharacter(uint characterId, String name)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE ffxiv_characters2 SET state=1 WHERE id=@cid AND name=@name";
cmd.Prepare();
cmd.Parameters.AddWithValue("@cid", characterId);
cmd.Parameters.AddWithValue("@name", name);
cmd.ExecuteNonQuery();
}
catch (MySqlException e)
{
}
finally
{
conn.Close();
}
}
}
public static List<World> getServers()
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
List<World> worldList = new List<World>();
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM ffxiv_servers WHERE isActive=true";
cmd.Prepare();
MySqlDataReader Reader = cmd.ExecuteReader();
if (!Reader.HasRows) return worldList;
while (Reader.Read())
{
var id = Reader.GetUInt16("id");
var name = Reader.GetString("name");
var address = Reader.GetString("address");
var port = Reader.GetUInt16("port");
var unknown = Reader.GetUInt16("unknown");
var numChars = Reader.GetUInt32("numChars");
var maxChars = Reader.GetUInt32("maxChars");
var isActive = Reader.GetBoolean("isActive");
if (isActive)
{
World world = new World();
world.id = id;
world.name = name;
world.address = address;
world.port = port;
world.unknown = unknown;
uint result = ((numChars / maxChars) *0xFF) & 0xFF;
world.population = (ushort)result;
world.isActive = isActive;
worldList.Add(world);
}
}
}
catch (MySqlException e)
{ }
finally
{
conn.Close();
}
return worldList;
}
}
public static World getServer(uint serverId)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
World world = null;
try
{
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM ffxiv_servers WHERE id=%serverId";
cmd.Prepare();
cmd.Parameters.AddWithValue("@serverId", serverId);
MySqlDataReader Reader = cmd.ExecuteReader();
if (!Reader.HasRows) return world;
while (Reader.Read())
{
var id = Reader.GetUInt16("id");
var name = Reader.GetString("name");
var address = Reader.GetString("address");
var port = Reader.GetUInt16("port");
var unknown = Reader.GetUInt16("unknown");
var numChars = Reader.GetUInt32("numChars");
var maxChars = Reader.GetUInt32("maxChars");
var isActive = Reader.GetBoolean("isActive");
if (isActive)
{
world = new World();
world.id = id;
world.name = name;
world.address = address;
world.port = port;
world.unknown = unknown;
uint result = ((numChars / maxChars) * 0xFF) & 0xFF;
world.population = (ushort)result;
world.isActive = isActive;
}
}
}
catch (MySqlException e)
{
}
finally
{
conn.Close();
}
return world;
}
}
}
}

View file

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" 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>{703091E0-F69C-4177-8FAE-C258AC6A65AA}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FFXIVClassic_Lobby_Server</RootNamespace>
<AssemblyName>FFXIVClassic_Lobby_Server</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</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>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</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>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="Cyotek.Collections.Generic.CircularBuffer">
<HintPath>..\packages\Cyotek.CircularBuffer.1.0.0.0\lib\net20\Cyotek.Collections.Generic.CircularBuffer.dll</HintPath>
</Reference>
<Reference Include="MySql.Data">
<HintPath>..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Character.cs" />
<Compile Include="ClientConnection.cs" />
<Compile Include="common\Blowfish.cs" />
<Compile Include="common\IniFile.cs" />
<Compile Include="common\Utils.cs" />
<Compile Include="ConfigConstants.cs" />
<Compile Include="Database.cs" />
<Compile Include="PacketProcessor.cs" />
<Compile Include="packets\BasePacket.cs" />
<Compile Include="packets\CharacterRequestPacket.cs" />
<Compile Include="packets\HardCoded_Packets.cs" />
<Compile Include="packets\SubPacket.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Server.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,227 @@
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Lobby_Server.packets;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server
{
class PacketProcessor
{
List<ClientConnection> mConnections;
Boolean isAlive = true;
public PacketProcessor(List<ClientConnection> connectionList)
{
mConnections = connectionList;
}
public void update()
{
Console.WriteLine("Packet processing thread has started");
while (isAlive)
{
lock (mConnections)
{
foreach (ClientConnection client in mConnections)
{
//Receive packets
while (true)
{
if (client.incomingStream.Size < BasePacket.BASEPACKET_SIZE)
break;
try {
if (client.incomingStream.Size < BasePacket.BASEPACKET_SIZE)
break;
BasePacketHeader header = BasePacket.getHeader(client.incomingStream.Peek(BasePacket.BASEPACKET_SIZE));
if (client.incomingStream.Size < header.packetSize)
break;
BasePacket packet = new BasePacket(client.incomingStream.Get(header.packetSize));
processPacket(client, packet);
}
catch(OverflowException)
{ break; }
}
//Send packets
while (client.sendPacketQueue.Count != 0)
client.flushQueuedSendPackets();
}
}
}
}
private void processPacket(ClientConnection client, BasePacket packet)
{
if ((packet.header.packetSize == 0x288) && (packet.data[0x34] == 'T')) //Test Ticket Data
{
//Crypto handshake
ProcessStartSession(client, packet);
return;
}
BasePacket.decryptPacket(client.blowfish, ref packet);
packet.debugPrintPacket();
List<SubPacket> subPackets = packet.getSubpackets();
foreach (SubPacket subpacket in subPackets)
{
switch (subpacket.header.opcode)
{
case 0x03:
ProcessGetCharacters(client, subpacket);
break;
case 0x04:
ProcessSelectCharacter(client, subpacket);
break;
case 0x05:
ProcessSessionAcknowledgement(client, subpacket);
break;
case 0x0B:
ProcessModifyCharacter(client, subpacket);
break;
default:
Debug.WriteLine("Unknown command 0x{0:X} received.", subpacket.header.opcode);
break;
}
}
}
private void ProcessStartSession(ClientConnection client, BasePacket packet)
{
UInt32 clientTime = BitConverter.ToUInt32(packet.data, 0x74);
//We assume clientTime is 0x50E0E812, but we need to generate a proper key later
byte[] blowfishKey = { 0xB4, 0xEE, 0x3F, 0x6C, 0x01, 0x6F, 0x5B, 0xD9, 0x71, 0x50, 0x0D, 0xB1, 0x85, 0xA2, 0xAB, 0x43};
client.blowfish = new Blowfish(blowfishKey);
Console.WriteLine("Received encryption key: 0x{0:X}", clientTime);
//Respond with acknowledgment
BasePacket outgoingPacket = new BasePacket(HardCoded_Packets.g_secureConnectionAcknowledgment);
BasePacket.encryptPacket(client.blowfish, outgoingPacket);
client.queuePacket(outgoingPacket);
}
private void ProcessSessionAcknowledgement(ClientConnection client, SubPacket packet)
{
//String sessionId = Utils.unsafeAsciiBytesToString(packet.data, 0x30);
//String clientVersion = Utils.unsafeAsciiBytesToString(packet.data, 0x70);
Debug.WriteLine("Got acknowledgment for secure session.");
// Debug.WriteLine("SESSION_ID: {0}", sessionId);
// Debug.WriteLine("CLIENT_VERSION: {0}", clientVersion);
//Check if got MYSQL Conn
//auto query = string_format("SELECT userId FROM ffxiv_sessions WHERE id = '%s' AND expiration > NOW()", sessionId.c_str());
//Console.WriteLine("UserId {0} logged in.", id);
BasePacket outgoingPacket = new BasePacket("./packets/loginAck.bin");
BasePacket.encryptPacket(client.blowfish, outgoingPacket);
client.queuePacket(outgoingPacket);
}
private void ProcessGetCharacters(ClientConnection client, SubPacket packet)
{
Console.WriteLine("{0} => Get characters", client.getAddress());
BasePacket outgoingPacket = new BasePacket("./packets/getCharsPacket.bin");
BasePacket.encryptPacket(client.blowfish, outgoingPacket);
client.queuePacket(outgoingPacket);
}
private void ProcessSelectCharacter(ClientConnection client, SubPacket packet)
{
uint characterId = 0;
using (BinaryReader binReader = new BinaryReader(new MemoryStream(packet.data)))
{
binReader.BaseStream.Seek(0x8, SeekOrigin.Begin);
characterId = binReader.ReadUInt32();
binReader.Close();
}
Console.WriteLine("{0} => Select character id {1}", client.getAddress(), characterId);
String serverIp = "141.117.162.99";
ushort port = 54992;
BitConverter.GetBytes(port);
BasePacket outgoingPacket = new BasePacket("./packets/selectChar.bin");
//Write Character ID and Server info
using (BinaryWriter binWriter = new BinaryWriter(new MemoryStream(outgoingPacket.data)))
{
binWriter.Seek(0x28, SeekOrigin.Begin);
binWriter.Write(characterId);
binWriter.Seek(0x78, SeekOrigin.Begin);
binWriter.Write(System.Text.Encoding.ASCII.GetBytes(serverIp));
binWriter.Seek(0x76, SeekOrigin.Begin);
binWriter.Write(port);
binWriter.Close();
}
BasePacket.encryptPacket(client.blowfish, outgoingPacket);
client.queuePacket(outgoingPacket);
}
private void ProcessModifyCharacter(ClientConnection client, SubPacket packet)
{
packet.debugPrintSubPacket();
CharacterRequestPacket.CharacterRequest charaReq = CharacterRequestPacket.toStruct(packet.data);
var slot = charaReq.slot;
var code = charaReq.command;
var name = charaReq.characterName;
var worldId = charaReq.worldId;
switch (code)
{
case 0x01://Reserve
//Database.reserveCharacter(0, slot, worldId, name);
//Confirm Reserve
BasePacket confirmReservePacket = new BasePacket("./packets/chara/confirmReserve.bin");
BasePacket.encryptPacket(client.blowfish, confirmReservePacket);
client.queuePacket(confirmReservePacket);
Console.WriteLine("Reserving character \"{0}\"", charaReq.characterName);
break;
case 0x02://Make
Character character = Character.EncodedToCharacter(charaReq.characterInfoEncoded);
Database.makeCharacter(0, name, character);
//Confirm
BasePacket confirmMakePacket = new BasePacket("./packets/chara/confirmMake.bin");
BasePacket.encryptPacket(client.blowfish, confirmMakePacket);
client.queuePacket(confirmMakePacket);
Console.WriteLine("Character created!");
break;
case 0x03://Rename
break;
case 0x04://Delete
Database.deleteCharacter(charaReq.characterId, charaReq.characterName);
//Confirm
BasePacket deleteConfirmPacket = new BasePacket("./packets/chara/confirmDelete.bin");
BasePacket.encryptPacket(client.blowfish, deleteConfirmPacket);
client.queuePacket(deleteConfirmPacket);
Console.WriteLine("Character deleted \"{0}\"", charaReq.characterName);
break;
case 0x06://Rename Retainer
break;
}
}
}
}

View file

@ -0,0 +1,73 @@
using FFXIVClassic_Lobby_Server.packets;
using System;
using System.Diagnostics;
using System.Threading;
using FFXIVClassic_Lobby_Server.common;
using System.Runtime.InteropServices;
using MySql.Data.MySqlClient;
using System.Reflection;
namespace FFXIVClassic_Lobby_Server
{
class Program
{
static void Main(string[] args)
{
#if DEBUG
TextWriterTraceListener myWriter = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(myWriter);
#endif
bool startServer = true;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("--------FFXIV 1.0 Lobby Server--------");
Console.ForegroundColor = ConsoleColor.Gray;
Assembly assem = Assembly.GetExecutingAssembly();
Version vers = assem.GetName().Version;
Console.WriteLine("Version: " + vers.ToString());
//Load Config
if (!ConfigConstants.load())
startServer = false;
//Test DB Connection
Console.Write("Testing DB connection... ");
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
conn.Close();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("[OK]");
Console.ForegroundColor = ConsoleColor.Gray;
}
catch (MySqlException e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[FAILED]");
Console.ForegroundColor = ConsoleColor.Gray;
startServer = false;
}
}
//Start Server if A-OK
if (startServer)
{
Server server = new Server();
server.startServer();
while (true) Thread.Sleep(10000);
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}

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("FFXIVClassic_Lobby_Server")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FFXIVClassic_Lobby_Server")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[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("9b87f719-1e4d-4781-89c9-28d99f579253")]
// 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.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Threading;
namespace FFXIVClassic_Lobby_Server
{
class Server
{
public const int FFXIV_LOBBY_PORT = 54994;
public const int BUFFER_SIZE = 0x400;
public const int BACKLOG = 100;
private Socket mServerSocket;
private List<ClientConnection> mConnectionList = new List<ClientConnection>();
private PacketProcessor mProcessor;
private Thread mProcessorThread;
#region Socket Handling
public bool startServer()
{
IPEndPoint serverEndPoint = new System.Net.IPEndPoint(IPAddress.Parse("141.117.161.40"), FFXIV_LOBBY_PORT);
try{
mServerSocket = new System.Net.Sockets.Socket(serverEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
}
catch (Exception e)
{
throw new ApplicationException("Could not create socket, check to make sure not duplicating port", e);
}
try
{
mServerSocket.Bind(serverEndPoint);
mServerSocket.Listen(BACKLOG);
}
catch (Exception e)
{
throw new ApplicationException("Error occured while binding socket, check inner exception", e);
}
try
{
mServerSocket.BeginAccept(new AsyncCallback(acceptCallback), mServerSocket);
}
catch (Exception e)
{
throw new ApplicationException("Error occured starting listeners, check inner exception", e);
}
Console.Write("Server has started @ ");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("{0}:{1}", (mServerSocket.LocalEndPoint as IPEndPoint).Address, (mServerSocket.LocalEndPoint as IPEndPoint).Port);
Console.ForegroundColor = ConsoleColor.Gray;
mProcessor = new PacketProcessor(mConnectionList);
mProcessorThread = new Thread(new ThreadStart(mProcessor.update));
mProcessorThread.Start();
return true;
}
private void acceptCallback(IAsyncResult result)
{
ClientConnection conn = null;
try
{
System.Net.Sockets.Socket s = (System.Net.Sockets.Socket)result.AsyncState;
conn = new ClientConnection();
conn.socket = s.EndAccept(result);
conn.buffer = new byte[BUFFER_SIZE];
lock (mConnectionList)
{
mConnectionList.Add(conn);
}
//Queue recieving of data from the connection
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn);
//Queue the accept of the next incomming connection
mServerSocket.BeginAccept(new AsyncCallback(acceptCallback), mServerSocket);
Console.WriteLine("Connection {0}:{1} has connected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
}
catch (SocketException)
{
if (conn.socket != null)
{
conn.socket.Close();
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
mServerSocket.BeginAccept(new AsyncCallback(acceptCallback), mServerSocket);
}
catch (Exception)
{
if (conn.socket != null)
{
conn.socket.Close();
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
mServerSocket.BeginAccept(new AsyncCallback(acceptCallback), mServerSocket);
}
}
private void receiveCallback(IAsyncResult result)
{
ClientConnection conn = (ClientConnection)result.AsyncState;
try
{
int bytesRead = conn.socket.EndReceive(result);
if (bytesRead > 0)
{
conn.processIncoming(bytesRead);
//Queue the next receive
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn);
}
else
{
Console.WriteLine("Connection {0}:{1} has disconnected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
conn.socket.Close();
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
}
catch (SocketException)
{
if (conn.socket != null)
{
Console.WriteLine("Connection @ {0}:{1} has disconnected.", (conn.socket.RemoteEndPoint as IPEndPoint).Address, (conn.socket.RemoteEndPoint as IPEndPoint).Port);
conn.socket.Close();
lock (mConnectionList)
{
mConnectionList.Remove(conn);
}
}
}
}
#endregion
#region Packet Handling
#endregion
}
}

View file

@ -0,0 +1,74 @@
using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace FFXIVClassic_Lobby_Server.common
{
public class Blowfish
{
[DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern short initializeBlowfish(byte[] key, short keySize, uint[] P, uint[,] S);
[DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void blowfish_encipher(ref int xl, ref int xr, uint[] P);
[DllImport("../../../Debug/Blowfish.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void blowfish_decipher(ref int xl, ref int xr, uint[] P);
private uint[] P = new uint[16+2];
private uint[,] S = new uint[4,256];
public Blowfish(byte[] key)
{
InitBlowfish(key);
}
private int InitBlowfish(byte[] key)
{
return initializeBlowfish(key, (short)key.Length, P, S);
}
public void Encipher(byte[] data, int offset, int length)
{
if ((length - offset) % 8 != 0)
throw new ArgumentException("Needs to be a multiple of 8");
for (int i = offset; i < offset+length; i+=8)
{
int xl = (data[i + 0]) | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24);
int xr = (data[i + 4]) | (data[i + 5] << 8) | (data[i + 6] << 16) | (data[i + 7] << 24);
blowfish_encipher(ref xl, ref xr, P);
data[i + 0] = (byte)(xl >> 0);
data[i + 1] = (byte)(xl >> 8);
data[i + 2] = (byte)(xl >> 16);
data[i + 3] = (byte)(xl >> 24);
data[i + 4] = (byte)(xr >> 0);
data[i + 5] = (byte)(xr >> 8);
data[i + 6] = (byte)(xr >> 16);
data[i + 7] = (byte)(xr >> 24);
}
}
public void Decipher(byte[] data, int offset, int length)
{
if ((length - offset) % 8 != 0)
throw new ArgumentException("Needs to be a multiple of 8");
for (int i = offset; i < offset + length; i += 8)
{
int xl = (data[i + 0]) | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24);
int xr = (data[i + 4]) | (data[i + 5] << 8) | (data[i + 6] << 16) | (data[i + 7] << 24);
blowfish_decipher(ref xl, ref xr, P);
data[i + 0] = (byte)(xl >> 0);
data[i + 1] = (byte)(xl >> 8);
data[i + 2] = (byte)(xl >> 16);
data[i + 3] = (byte)(xl >> 24);
data[i + 4] = (byte)(xr >> 0);
data[i + 5] = (byte)(xr >> 8);
data[i + 6] = (byte)(xr >> 16);
data[i + 7] = (byte)(xr >> 24);
}
}
}
}

View file

@ -0,0 +1,63 @@
using System.Runtime.InteropServices;
using System.Text;
namespace FFXIVClassic_Lobby_Server.common
{
/// <summary>
/// Create a New INI file to store or load data
/// </summary>
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);
/// <summary>
/// INIFile Constructor.
/// </summary>
/// <PARAM name="INIPath"></PARAM>
public IniFile(string INIPath)
{
path = INIPath;
}
/// <summary>
/// Write Data to the INI File
/// </summary>
/// <PARAM name="Section"></PARAM>
/// Section name
/// <PARAM name="Key"></PARAM>
/// Key Name
/// <PARAM name="Value"></PARAM>
/// Value Name
public void IniWriteValue(string Section, string Key, string Value)
{
WritePrivateProfileString(Section, Key, Value, this.path);
}
/// <summary>
/// Read Data Value From the Ini File
/// </summary>
/// <PARAM name="Section"></PARAM>
/// <PARAM name="Key"></PARAM>
/// <PARAM name="Path"></PARAM>
/// <returns></returns>
public string IniReadValue(string Section, string Key)
{
StringBuilder temp = new StringBuilder(255);
int i = GetPrivateProfileString(Section, Key, "", temp,
255, this.path);
return temp.ToString();
}
}
}

View file

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.common
{
static class Utils
{
private static readonly uint[] _lookup32 = CreateLookup32();
private static uint[] CreateLookup32()
{
var result = new uint[256];
for (int i = 0; i < 256; i++)
{
string s = i.ToString("X2");
result[i] = ((uint)s[0]) + ((uint)s[1] << 16);
}
return result;
}
public static string ByteArrayToHex(byte[] bytes)
{
var lookup32 = _lookup32;
var result = new char[(bytes.Length * 3) + ((bytes.Length/16) < 1 ? 1 : (bytes.Length/16)*3) + bytes.Length+60];
int numNewLines = 0;
for (int i = 0; i < bytes.Length; i++)
{
var val = lookup32[bytes[i]];
result[(3 * i) + (17 * numNewLines) + 0] = (char)val;
result[(3 * i) + (17 * numNewLines) + 1] = (char)(val >> 16);
result[(3 * i) + (17 * numNewLines) + 2] = ' ';
result[(numNewLines * (3*16+17)) + (3 * 16) + (i % 16)] = (char)bytes[i] >= 32 && (char)bytes[i] <= 126 ? (char)bytes[i] : '.';
if (i != bytes.Length - 1 && bytes.Length > 16 && i != 0 && (i+1) % 16 == 0)
{
result[(numNewLines * (3*16+17)) + (3 * 16) + (16)] = '\n';
numNewLines++;
}
}
return new string(result);
}
/*
/// <summary>
/// Grabs an instance of a class directly from the MySqlDataReader instead of manually building from GetString() etc.
/// </summary>
/// <typeparam name="T">Type of object to return</typeparam>
/// <param name="reader">Reader instance</param>
/// <returns>new T</returns>
public static T GetSQLObject<T>(this MySql.Data.MySqlClient.MySqlDataReader reader)
{
var obj = Activator.CreateInstance(typeof(T));
var fields = obj.GetType().GetFields();
foreach (var field in obj.GetType().GetFields())
{
var attrs = field.GetCustomAttributes(typeof(System.Runtime.Serialization.DataMemberAttribute), true) as System.Runtime.Serialization.DataMemberAttribute[];
if (attrs.Length == 0)
{
continue;
}
var fieldID = attrs[0].Name;
var fieldType = field.FieldType;
field.SetValue(obj, reader.GetValue(reader.GetOrdinal(fieldID)));
}
return (T)obj;
}*/
}
}

View file

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Lobby_Server.common;
namespace FFXIVClassic_Lobby_Server
{
class Character
{
public string name = "";
public uint id = 0;
public uint tribe = 0;
public uint size = 0;
public uint voice = 0;
public uint skinColor = 0;
public uint hairStyle = 0;
public uint hairColor = 0;
public uint eyeColor = 0;
public uint faceType = 0;
public uint faceBrow = 0;
public uint faceEye = 0;
public uint faceIris = 0;
public uint faceNose = 0;
public uint faceMouth = 0;
public uint faceJaw = 0;
public uint faceCheek = 0;
public uint faceOption1 = 0;
public uint faceOption2 = 0;
public uint guardian = 0;
public uint birthMonth = 0;
public uint birthDay = 0;
public uint allegiance = 0;
public uint weapon1 = 0;
public uint weapon2 = 0;
public uint headGear = 0;
public uint bodyGear = 0;
public uint legsGear = 0;
public uint handsGear = 0;
public uint feetGear = 0;
public uint waistGear = 0;
public uint rightEarGear = 0;
public uint leftEarGear = 0;
public uint rightFingerGear = 0;
public uint leftFingerGear = 0;
public byte[] toBytes()
{
byte[] bytes = new byte[0x120];
return bytes;
}
public static String characterToEncoded(Character chara)
{
String charaInfo = System.Convert.ToBase64String(chara.toBytes());
charaInfo.Replace("+", "-");
charaInfo.Replace("/", "_");
return charaInfo;
}
public static Character EncodedToCharacter(String charaInfo)
{
charaInfo.Replace("+", "-");
charaInfo.Replace("/", "_");
byte[] data = System.Convert.FromBase64String(charaInfo);
Console.WriteLine("------------Base64 printout------------------");
Console.WriteLine(Utils.ByteArrayToHex(data));
Console.WriteLine("------------Base64 printout------------------");
Character chara = new Character();
return chara;
}
}
}

View file

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.dataobjects
{
class World
{
public ushort id;
public string address;
public ushort port;
public ushort unknown;
public ushort population;
public string name;
public bool isActive;
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cyotek.CircularBuffer" version="1.0.0.0" targetFramework="net45" />
<package id="MySql.Data" version="6.9.7" targetFramework="net45" />
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
</packages>

View file

@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
using FFXIVClassic_Lobby_Server.common;
using System.IO;
namespace FFXIVClassic_Lobby_Server.packets
{
[StructLayout(LayoutKind.Sequential)]
public struct BasePacketHeader
{
public byte isAuthenticated;
public byte isEncrypted;
public ushort reserved;
public ushort packetSize;
public ushort numSubpackets;
public uint unknown1; //Id?
public uint unknown2; //Usually 0x13B
}
public class BasePacket{
public const int BASEPACKET_SIZE = 0x10;
public BasePacketHeader header;
public byte[] data;
public unsafe BasePacket(String path)
{
byte[] bytes = File.ReadAllBytes(path);
if (bytes.Length < BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[0])
{
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
}
if (bytes.Length < header.packetSize)
throw new OverflowException("Packet Error: Packet size didn't equal given size");
int packetSize = header.packetSize;
data = new byte[packetSize - BASEPACKET_SIZE];
Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
}
public unsafe BasePacket(byte[] bytes)
{
if (bytes.Length < BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[0])
{
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
}
if (bytes.Length < header.packetSize)
throw new OverflowException("Packet Error: Packet size didn't equal given size");
int packetSize = header.packetSize;
data = new byte[packetSize - BASEPACKET_SIZE];
Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
}
public unsafe BasePacket(byte[] bytes, ref int offset)
{
if (bytes.Length < offset + BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[offset])
{
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
}
int packetSize = header.packetSize;
if (bytes.Length < offset + header.packetSize)
throw new OverflowException("Packet Error: Packet size didn't equal given size");
data = new byte[packetSize - BASEPACKET_SIZE];
Array.Copy(bytes, offset + BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
offset += packetSize;
}
public BasePacket(BasePacketHeader header, byte[] data)
{
this.header = header;
this.data = data;
}
public List<SubPacket> getSubpackets()
{
List<SubPacket> subpackets = new List<SubPacket>(header.numSubpackets);
int offset = 0;
while (offset < data.Length)
subpackets.Add(new SubPacket(data, ref offset));
return subpackets;
}
public unsafe static BasePacketHeader getHeader(byte[] bytes)
{
BasePacketHeader header;
if (bytes.Length < BASEPACKET_SIZE)
throw new OverflowException("Packet Error: Packet was too small");
fixed (byte* pdata = &bytes[0])
{
header = (BasePacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(BasePacketHeader));
}
return header;
}
public byte[] getHeaderBytes()
{
int size = Marshal.SizeOf(header);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public byte[] getPacketBytes()
{
byte[] outBytes = new byte[header.packetSize];
Array.Copy(getHeaderBytes(), 0, outBytes, 0, BASEPACKET_SIZE);
Array.Copy(data, 0, outBytes, BASEPACKET_SIZE, data.Length);
return outBytes;
}
#region Utility Functions
public BasePacket createPacket(List<SubPacket> subpackets, bool isAuthed, bool isEncrypted)
{
//Create Header
BasePacketHeader header = new BasePacketHeader();
byte[] data = null;
header.isAuthenticated = isAuthed?(byte)1:(byte)0;
header.isEncrypted = isEncrypted?(byte)1:(byte)0;
header.numSubpackets = (ushort)subpackets.Count;
header.packetSize = BASEPACKET_SIZE;
//Get packet size
foreach (SubPacket subpacket in subpackets)
header.packetSize += subpacket.header.subpacketSize;
data = new byte[header.packetSize-0x10];
//Add Subpackets
int offset = 0;
foreach (SubPacket subpacket in subpackets)
{
byte[] subpacketData = subpacket.getBytes();
Array.Copy(subpacketData, 0, data, offset, subpacketData.Length);
header.packetSize += (ushort)subpacketData.Length;
}
Debug.Assert(data != null && offset == data.Length && header.packetSize == 0x10 + offset);
BasePacket packet = new BasePacket(header, data);
return packet;
}
public static unsafe void encryptPacket(Blowfish blowfish, BasePacket packet)
{
byte[] data = packet.data;
int size = packet.header.packetSize;
int offset = 0;
while (offset < data.Length) {
if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
throw new OverflowException("Packet Error: Subpacket was too small");
SubPacketHeader header;
fixed (byte* pdata = &data[offset])
{
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
}
if (data.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
blowfish.Encipher(data, offset + 0x10, header.subpacketSize-0x10);
offset += header.subpacketSize;
}
}
public static unsafe void decryptPacket(Blowfish blowfish, ref BasePacket packet)
{
byte[] data = packet.data;
int size = packet.header.packetSize;
int offset = 0;
while (offset < data.Length)
{
if (data.Length < offset + SubPacket.SUBPACKET_SIZE)
throw new OverflowException("Packet Error: Subpacket was too small");
SubPacketHeader header;
fixed (byte* pdata = &data[offset])
{
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
}
if (data.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
blowfish.Decipher(data, offset + 0x10, header.subpacketSize-0x10);
offset += header.subpacketSize;
}
}
#endregion
public void debugPrintPacket()
{
#if DEBUG
Console.BackgroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("IsAuthed: {0}, IsEncrypted: {1}, Size: 0x{2:X}, Num Subpackets: {3}", header.isAuthenticated, header.isEncrypted, header.packetSize, header.numSubpackets);
Console.WriteLine("{0}", Utils.ByteArrayToHex(getHeaderBytes()));
foreach (SubPacket sub in getSubpackets())
sub.debugPrintSubPacket();
Console.BackgroundColor = ConsoleColor.Black;
#endif
}
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.packets
{
class CharacterRequestPacket
{
[StructLayout(LayoutKind.Sequential)]
public unsafe struct CharacterRequest
{
public uint sequence;
public uint unknown;
public uint characterId;
public uint unknown2;
public byte slot;
public byte command;
public ushort worldId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public String characterName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x190)]
public String characterInfoEncoded;
}
public static unsafe CharacterRequest toStruct(byte[] bytes)
{
fixed (byte* pdata = &bytes[0])
{
return (CharacterRequest)Marshal.PtrToStructure(new IntPtr(pdata), typeof(CharacterRequest));
}
}
}
}

View file

@ -0,0 +1,266 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Lobby_Server.packets
{
class HardCoded_Packets
{
public static byte[] g_secureConnectionAcknowledgment =
{
0x00, 0x00, 0x00, 0x00, 0xA0, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x90, 0x02, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x69, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xED, 0x45, 0x02, 0x00, 0x00, 0x00, 0x00,
0xC0, 0xED, 0xDF, 0xFF, 0xAF, 0xF7, 0xF7, 0xAF, 0x10, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0x42, 0x82, 0x63, 0x52, 0x01, 0x00, 0x00, 0x00, 0x10, 0xEF, 0xDF, 0xFF, 0x53, 0x61, 0x6D, 0x70,
0x6C, 0x65, 0x20, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x20, 0x52, 0x75, 0x6E, 0x52, 0x75, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0xF7, 0xAF, 0xAF, 0xF7, 0x00, 0x00, 0xB8, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, 0x2C, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x61, 0x6C, 0x6C, 0x62, 0x61, 0x63, 0x6B, 0x4F, 0x62,
0x6A, 0x65, 0x63, 0x74, 0x2E, 0x2E, 0x2E, 0x5B, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39,
0x39, 0x2E, 0x38, 0x32, 0x3A, 0x36, 0x33, 0x34, 0x30, 0x37, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00,
0x70, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x6C, 0x4E, 0x38, 0x01, 0x00, 0x00, 0x00, 0x00,
0x32, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xAF, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC0, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xFE, 0x4E, 0x38, 0x01, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x20, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0x00, 0x01, 0xCC, 0xCC, 0x0C, 0x69, 0x00, 0xE0, 0xD0, 0x58, 0x33, 0x02, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x80, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0xC0, 0xEE, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xD0, 0xED, 0x45, 0x02, 0x00, 0x00, 0x00, 0x00,
0xF0, 0xEE, 0xDF, 0xFF, 0xAF, 0xF7, 0xF7, 0xAF, 0x20, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF,
0x0C, 0x69, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x34, 0x30, 0x37, 0x00, 0x00, 0x00, 0x00,
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x18, 0xBE, 0x34, 0x01, 0x00, 0x00, 0x00, 0x00,
0xD8, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0xF7, 0xAF, 0x42, 0x82, 0x63, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39, 0x39, 0x2E, 0x38, 0x32, 0x00, 0x00,
0x00, 0x00, 0x36, 0x36, 0x2E, 0x31, 0x33, 0x30, 0x2E, 0x39, 0x39, 0x2E, 0x38, 0x32, 0x00, 0xFF,
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x24, 0xCF, 0x76, 0x01, 0x00, 0x00, 0x00, 0x00,
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x70, 0x7A, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
0x90, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xD1, 0xF3, 0x37, 0x01, 0x00, 0x00, 0x00, 0x00,
0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0xC0, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0xE8, 0x3E, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
0x70, 0x99, 0xAA, 0x01, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0x58, 0x59, 0x33, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6C, 0x4D, 0x02, 0x00, 0x00, 0x00, 0x00,
0xE0, 0xEF, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x05, 0x3F, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x69, 0x00, 0xE0, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0xF0, 0xDF, 0xFF, 0x7F, 0xFD, 0xFF, 0xFF, 0x23, 0x3F, 0x77, 0x01, 0x00, 0x00, 0x00, 0x00,
0xC0, 0x5A, 0x33, 0x02, 0x0C, 0x69, 0x00, 0xE0, 0xA0, 0x32, 0xAC, 0x01, 0x00, 0x00, 0x00, 0x00,
};
public static byte[] g_characterListPacket =
{
0x01, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x05, 0x00, 0xEA, 0xCE, 0x8A, 0xEE, 0x3B, 0x01, 0x00, 0x00,
0x10, 0x02, 0x03, 0x00, 0x68, 0x68, 0x00, 0xE0, 0x68, 0x68, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xE8, 0xE0, 0x50, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(9) 0x06 -> Item Count
0x02, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x44, 0x75, 0x72, 0x61, 0x6E, 0x64, 0x61, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x01, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x79, 0x70, 0x65, 0x72, 0x69, 0x6F, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x02, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x4D, 0x61, 0x73, 0x61, 0x6D, 0x75, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x03, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x47, 0x75, 0x6E, 0x67, 0x6E, 0x69, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0x00, 0x04, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x41, 0x65, 0x67, 0x69, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0A, 0x00, 0x05, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x53, 0x61, 0x72, 0x67, 0x61, 0x74, 0x61, 0x6E, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x02, 0x03, 0x00, 0x68, 0x68, 0x00, 0xE0, 0x68, 0x68, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xE8, 0xE0, 0x50, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0B, 0x00, 0x06, 0x00, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x42, 0x61, 0x6C, 0x6D, 0x75, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0C, 0x00, 0x07, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x52, 0x69, 0x64, 0x69, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x08, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x45, 0x78, 0x63, 0x61, 0x6C, 0x69, 0x62, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x09, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x52, 0x61, 0x67, 0x6E, 0x61, 0x72, 0x6F, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x10, 0x02, 0x03, 0x00, 0x68, 0x68, 0x00, 0xE0, 0x68, 0x68, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xE8, 0xE0, 0x50, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xF0, 0x01, 0x03, 0x00, 0x68, 0x68, 0x00, 0xE0, 0x68, 0x68, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xE8, 0xE0, 0x50, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0xF5, 0xA5, 0xE0,
0x09, 0x79, 0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x72, 0x6E, //(C) "Cornelius" Retainer Name
0x65, 0x6C, 0x69, 0x75, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x09, 0x79, 0xC1, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xD0, 0x03, 0x03, 0x00, 0x68, 0x68, 0x00, 0xE0, 0x68, 0x68, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x14, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xE8, 0xE0, 0x50, 0x00, 0x00, 0x00, 0x00, //(8) 0x50E0E824 -> Timestamp
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xFC, 0xE7, 0x58, 0x01, 0x09, 0x79, 0xC1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, //(4) 0x00C17909 -> Character Id
// 0x57, 0x72, 0x65, 0x6E, 0x69, 0x78, 0x20, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, //(0) "Wrenix Wrong" -> Character Name
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x52, 0x61, 0x67, 0x6E, 0x61, 0x72, 0x6F, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(0) "Ragnarok" -> Server Name
0x77, 0x41, 0x51, 0x41, 0x41, 0x4F, 0x6F, 0x6E, 0x49, 0x79, 0x4D, 0x4E, 0x41, 0x41, 0x41, 0x41, //(0) base64 stuff -> Character Data
0x56, 0x33, 0x4A, 0x6C, 0x62, 0x6D, 0x6C, 0x34, 0x49, 0x46, 0x64, 0x79, 0x62, 0x32, 0x35, 0x6E,
0x41, 0x42, 0x77, 0x41, 0x41, 0x41, 0x41, 0x45, 0x41, 0x41, 0x41, 0x41, 0x41, 0x77, 0x41, 0x41,
0x41, 0x41, 0x4D, 0x41, 0x41, 0x41, 0x41, 0x5F, 0x38, 0x4F, 0x41, 0x44, 0x41, 0x41, 0x48, 0x51,
0x46, 0x41, 0x41, 0x45, 0x41, 0x41, 0x41, 0x42, 0x41, 0x41, 0x41, 0x41, 0x41, 0x42, 0x54, 0x51,
0x43, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x47, 0x45, 0x67, 0x41, 0x41, 0x41, 0x41, 0x4D, 0x51, 0x41, 0x41, 0x51, 0x43, 0x51, 0x41,
0x41, 0x4D, 0x41, 0x73, 0x41, 0x41, 0x43, 0x4B, 0x56, 0x41, 0x41, 0x41, 0x41, 0x50, 0x67, 0x43,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x43, 0x51, 0x41,
0x41, 0x41, 0x41, 0x6B, 0x41, 0x77, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x4E, 0x76, 0x62, 0x31, 0x4D, 0x30, 0x35, 0x41, 0x51, 0x41, 0x41, 0x42, 0x42, 0x6F, 0x41,
0x41, 0x41, 0x45, 0x41, 0x42, 0x71, 0x6F, 0x69, 0x49, 0x75, 0x49, 0x4B, 0x41, 0x41, 0x41, 0x41,
0x63, 0x48, 0x4A, 0x32, 0x4D, 0x45, 0x6C, 0x75, 0x62, 0x6A, 0x41, 0x78, 0x41, 0x42, 0x45, 0x41,
0x41, 0x41, 0x42, 0x6B, 0x5A, 0x57, 0x5A, 0x68, 0x64, 0x57, 0x78, 0x30, 0x56, 0x47, 0x56, 0x79,
0x63, 0x6D, 0x6C, 0x30, 0x62, 0x33, 0x4A, 0x35, 0x41, 0x41, 0x77, 0x4A, 0x41, 0x68, 0x63, 0x41,
0x42, 0x41, 0x41, 0x41, 0x41, 0x41, 0x51, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x67, 0x41, 0x41, 0x41, 0x41, 0x49, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0xFD, 0xE7, 0x58, 0x01, 0x0B, 0x79, 0xC1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(4) 0x00C17909 -> Character Id (8) -> 0x72 = Legacy account?
// 0x59, 0x72, 0x65, 0x6E, 0x69, 0x78, 0x20, 0x57, 0x72, 0x6F, 0x6E, 0x67, 0x00, 0x00, 0x00, 0x00, //(0) "Wrenix Wrong" -> Character Name
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(4) 0x00C17909 -> Character Id (8) -> 0x72 = Legacy account?
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(0) "Wrenix Wrong" -> Character Name
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x52, 0x61, 0x67, 0x6E, 0x61, 0x72, 0x6F, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(0) "Ragnarok" -> Server Name
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
}
}

View file

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using FFXIVClassic_Lobby_Server;
using FFXIVClassic_Lobby_Server.common;
namespace FFXIVClassic_Lobby_Server.packets
{
[StructLayout(LayoutKind.Sequential)]
public struct SubPacketHeader
{
public ushort subpacketSize;
public ushort unknown0; //Always 0x03
public uint sourceId;
public uint targetId;
public uint unknown1;
public ushort unknown4; //Always 0x13
public ushort opcode;
public uint unknown5;
public uint timestamp;
public uint unknown6;
}
public class SubPacket
{
public const int SUBPACKET_SIZE = 0x20;
public SubPacketHeader header;
public byte[] data;
public unsafe SubPacket(byte[] bytes, ref int offset)
{
if (bytes.Length < offset + SUBPACKET_SIZE)
throw new OverflowException("Packet Error: Subpacket was too small");
fixed (byte* pdata = &bytes[offset])
{
header = (SubPacketHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(SubPacketHeader));
}
if (bytes.Length < offset + header.subpacketSize)
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
data = new byte[header.subpacketSize - SUBPACKET_SIZE];
Array.Copy(bytes, offset + SUBPACKET_SIZE, data, 0, data.Length);
offset += header.subpacketSize;
}
public byte[] getHeaderBytes()
{
int size = Marshal.SizeOf(header);
byte[] arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
public byte[] getBytes()
{
byte[] outBytes = new byte[header.subpacketSize];
Array.Copy(getHeaderBytes(), 0, outBytes, 0, SUBPACKET_SIZE);
Array.Copy(data, 0, outBytes, SUBPACKET_SIZE, data.Length);
return outBytes;
}
public void debugPrintSubPacket()
{
#if DEBUG
Console.BackgroundColor = ConsoleColor.DarkRed;
Console.WriteLine("Size: 0x{0:X}, Opcode: 0x{1:X}", header.subpacketSize, header.opcode);
Console.WriteLine("{0}", Utils.ByteArrayToHex(getHeaderBytes()));
Console.BackgroundColor = ConsoleColor.DarkMagenta;
Console.WriteLine("{0}", Utils.ByteArrayToHex(data));
Console.BackgroundColor = ConsoleColor.Black;
#endif
}
}
}