mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-04-02 19:42:05 -04:00
Merged the master branch of the Lobby server repo, into the map server repo. There is no reason to have two separate repos and the lobby server project will be now under the whole FFXIV Classic VS solution.
This commit is contained in:
commit
978f31e64a
7
.gitignore
vendored
7
.gitignore
vendored
@ -2,3 +2,10 @@ FFXIVClassic Map Server/bin/Debug/packets/wireshark packets/
|
|||||||
FFXIVClassic Map Server/bin/
|
FFXIVClassic Map Server/bin/
|
||||||
FFXIVClassic Map Server/obj/
|
FFXIVClassic Map Server/obj/
|
||||||
config.ini
|
config.ini
|
||||||
|
FFXIVClassic_Lobby_Server/bin/
|
||||||
|
FFXIVClassic_Lobby_Server/obj/
|
||||||
|
ipch/
|
||||||
|
Blowfish/Debug/
|
||||||
|
Debug/
|
||||||
|
FFXIVClassic_Lobby_Server.opensdf
|
||||||
|
FFXIVClassic_Lobby_Server.sdf
|
||||||
|
82
FFXIVClassic_Lobby_Server/ClientConnection.cs
Normal file
82
FFXIVClassic_Lobby_Server/ClientConnection.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
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);
|
||||||
|
public int lastPartialSize = 0;
|
||||||
|
|
||||||
|
//Instance Stuff
|
||||||
|
public uint currentUserId = 0;
|
||||||
|
public uint currentAccount;
|
||||||
|
public string currentSessionToken;
|
||||||
|
|
||||||
|
//Chara Creation
|
||||||
|
public string newCharaName;
|
||||||
|
public uint newCharaPid;
|
||||||
|
public uint newCharaCid;
|
||||||
|
public ushort newCharaSlot;
|
||||||
|
public ushort newCharaWorldId;
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
if (!socket.Connected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{ Log.error(String.Format("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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
FFXIVClassic_Lobby_Server/ConfigConstants.cs
Normal file
53
FFXIVClassic_Lobby_Server/ConfigConstants.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using FFXIVClassic_Lobby_Server.common;
|
||||||
|
using STA.Settings;
|
||||||
|
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 String OPTIONS_BINDIP;
|
||||||
|
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 configIni = new INIFile("./config.ini");
|
||||||
|
|
||||||
|
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]");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
472
FFXIVClassic_Lobby_Server/Database.cs
Normal file
472
FFXIVClassic_Lobby_Server/Database.cs
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
using FFXIVClassic_Lobby_Server.dataobjects;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using Dapper;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
//charState: 0 - Reserved, 1 - Inactive, 2 - Active
|
||||||
|
|
||||||
|
class Database
|
||||||
|
{
|
||||||
|
public static uint getUserIdFromSession(String sessionId)
|
||||||
|
{
|
||||||
|
uint id = 0;
|
||||||
|
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("SELECT * FROM sessions WHERE id = @sessionId AND expiration > NOW()", conn);
|
||||||
|
cmd.Parameters.AddWithValue("@sessionId", sessionId);
|
||||||
|
using (MySqlDataReader Reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (Reader.Read())
|
||||||
|
{
|
||||||
|
id = Reader.GetUInt32("userId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{ Console.WriteLine(e); }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool reserveCharacter(uint userId, uint slot, uint serverId, String name, out uint pid, out uint cid)
|
||||||
|
{
|
||||||
|
bool alreadyExists = false;
|
||||||
|
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();
|
||||||
|
|
||||||
|
//Check if exists
|
||||||
|
MySqlCommand cmd = new MySqlCommand("SELECT * FROM characters WHERE name=@name AND serverId=@serverId AND state != 2 AND state != 1", conn);
|
||||||
|
cmd.Parameters.AddWithValue("@serverId", serverId);
|
||||||
|
cmd.Parameters.AddWithValue("@name", name);
|
||||||
|
using (MySqlDataReader Reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
if (Reader.HasRows)
|
||||||
|
{
|
||||||
|
alreadyExists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Reserve
|
||||||
|
if (!alreadyExists)
|
||||||
|
{
|
||||||
|
MySqlCommand cmd2 = new MySqlCommand();
|
||||||
|
cmd2.Connection = conn;
|
||||||
|
cmd2.CommandText = "INSERT INTO characters(userId, slot, serverId, name, state) VALUES(@userId, @slot, @serverId, @name, 0)";
|
||||||
|
cmd2.Prepare();
|
||||||
|
cmd2.Parameters.AddWithValue("@userId", userId);
|
||||||
|
cmd2.Parameters.AddWithValue("@slot", slot);
|
||||||
|
cmd2.Parameters.AddWithValue("@serverId", serverId);
|
||||||
|
cmd2.Parameters.AddWithValue("@name", name);
|
||||||
|
cmd2.ExecuteNonQuery();
|
||||||
|
cid = (ushort)cmd2.LastInsertedId;
|
||||||
|
pid = 0xBABE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pid = 0;
|
||||||
|
cid = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{
|
||||||
|
pid = 0;
|
||||||
|
cid = 0;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.database(String.Format("CID={0} created on 'characters' table.", cid));
|
||||||
|
}
|
||||||
|
|
||||||
|
return alreadyExists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void makeCharacter(uint accountId, uint cid, CharaInfo charaInfo)
|
||||||
|
{
|
||||||
|
//Update character entry
|
||||||
|
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 characters SET
|
||||||
|
state=2,
|
||||||
|
currentZoneId=@zoneId,
|
||||||
|
positionX=@x,
|
||||||
|
positionY=@y,
|
||||||
|
positionZ=@z,
|
||||||
|
rotation=@r,
|
||||||
|
guardian=@guardian,
|
||||||
|
birthDay=@birthDay,
|
||||||
|
birthMonth=@birthMonth,
|
||||||
|
initialTown=@initialTown,
|
||||||
|
tribe=@tribe,
|
||||||
|
currentClassJob=@currentClass
|
||||||
|
WHERE userId=@userId AND id=@cid;
|
||||||
|
|
||||||
|
INSERT INTO characters_appearance
|
||||||
|
(characterId, baseId, size, voice, skinColor, hairStyle, hairColor, hairHighlightColor, eyeColor, faceType, faceEyebrows, faceEyeShape, faceIrisSize, faceNose, faceMouth, faceFeatures, ears, characteristics, characteristicsColor, mainhand, head, body, hands, legs, feet, waist)
|
||||||
|
VALUES
|
||||||
|
(@cid, 4294967295, @size, @voice, @skinColor, @hairStyle, @hairColor, @hairHighlightColor, @eyeColor, @faceType, @faceEyebrows, @faceEyeShape, @faceIrisSize, @faceNose, @faceMouth, @faceFeatures, @ears, @characteristics, @characteristicsColor, @mainhand, @head, @body, @hands, @legs, @feet, @waist)
|
||||||
|
";
|
||||||
|
cmd.Parameters.AddWithValue("@userId", accountId);
|
||||||
|
cmd.Parameters.AddWithValue("@cid", cid);
|
||||||
|
cmd.Parameters.AddWithValue("@guardian", charaInfo.guardian);
|
||||||
|
cmd.Parameters.AddWithValue("@birthDay", charaInfo.birthDay);
|
||||||
|
cmd.Parameters.AddWithValue("@birthMonth", charaInfo.birthMonth);
|
||||||
|
cmd.Parameters.AddWithValue("@initialTown", charaInfo.initialTown);
|
||||||
|
cmd.Parameters.AddWithValue("@tribe", charaInfo.tribe);
|
||||||
|
cmd.Parameters.AddWithValue("@currentClass", charaInfo.currentClass);
|
||||||
|
|
||||||
|
cmd.Parameters.AddWithValue("@zoneId", charaInfo.zoneId);
|
||||||
|
cmd.Parameters.AddWithValue("@x", charaInfo.x);
|
||||||
|
cmd.Parameters.AddWithValue("@y", charaInfo.y);
|
||||||
|
cmd.Parameters.AddWithValue("@z", charaInfo.z);
|
||||||
|
cmd.Parameters.AddWithValue("@r", charaInfo.rot);
|
||||||
|
|
||||||
|
cmd.Parameters.AddWithValue("@size", charaInfo.appearance.size);
|
||||||
|
cmd.Parameters.AddWithValue("@voice", charaInfo.appearance.voice);
|
||||||
|
cmd.Parameters.AddWithValue("@skinColor", charaInfo.appearance.skinColor);
|
||||||
|
cmd.Parameters.AddWithValue("@hairStyle", charaInfo.appearance.hairStyle);
|
||||||
|
cmd.Parameters.AddWithValue("@hairColor", charaInfo.appearance.hairColor);
|
||||||
|
cmd.Parameters.AddWithValue("@hairHighlightColor", charaInfo.appearance.hairHighlightColor);
|
||||||
|
cmd.Parameters.AddWithValue("@eyeColor", charaInfo.appearance.eyeColor);
|
||||||
|
cmd.Parameters.AddWithValue("@faceType", charaInfo.appearance.faceType);
|
||||||
|
cmd.Parameters.AddWithValue("@faceEyebrows", charaInfo.appearance.faceEyebrows);
|
||||||
|
cmd.Parameters.AddWithValue("@faceEyeShape", charaInfo.appearance.faceEyeShape);
|
||||||
|
cmd.Parameters.AddWithValue("@faceIrisSize", charaInfo.appearance.faceIrisSize);
|
||||||
|
cmd.Parameters.AddWithValue("@faceNose", charaInfo.appearance.faceNose);
|
||||||
|
cmd.Parameters.AddWithValue("@faceMouth", charaInfo.appearance.faceMouth);
|
||||||
|
cmd.Parameters.AddWithValue("@faceFeatures", charaInfo.appearance.faceFeatures);
|
||||||
|
cmd.Parameters.AddWithValue("@ears", charaInfo.appearance.ears);
|
||||||
|
cmd.Parameters.AddWithValue("@characteristics", charaInfo.appearance.characteristics);
|
||||||
|
cmd.Parameters.AddWithValue("@characteristicsColor", charaInfo.appearance.characteristicsColor);
|
||||||
|
|
||||||
|
cmd.Parameters.AddWithValue("@mainhand", charaInfo.weapon1);
|
||||||
|
cmd.Parameters.AddWithValue("@offhand", charaInfo.weapon2);
|
||||||
|
cmd.Parameters.AddWithValue("@head", charaInfo.head);
|
||||||
|
cmd.Parameters.AddWithValue("@body", charaInfo.body);
|
||||||
|
cmd.Parameters.AddWithValue("@legs", charaInfo.legs);
|
||||||
|
cmd.Parameters.AddWithValue("@hands", charaInfo.hands);
|
||||||
|
cmd.Parameters.AddWithValue("@feet", charaInfo.feet);
|
||||||
|
cmd.Parameters.AddWithValue("@waist", charaInfo.belt);
|
||||||
|
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.database(String.Format("CID={0} state updated to active(2).", cid));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create appearance entry
|
||||||
|
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 appearance(characterId, baseId, tribe, size, voice, skinColor, hairStyle, hairColor, hairHighlightColor, eyeColor, faceType, faceEyebrows, faceEyeShape, faceIrisSize, faceNose, faceMouth, faceFeatures, ears, characteristics, characteristicsColor, mainhand, offhand, head, body, hands, legs, feet, waist, leftFinger, rightFinger, leftEar, rightEar) VALUES(@characterId, @baseId, @tribe, @size, @voice, @skinColor, @hairStyle, @hairColor, @hairHighlightColor, @eyeColor, @faceType, @faceEyebrows, @faceEyeShape, @faceIrisSize, @faceNose, @faceMouth, @faceFeatures, @ears, @characteristics, @characteristicsColor, @mainhand, @offhand, @head, @body, @hands, @legs, @feet, @waist, @leftFinger, @rightFinger, @leftEar, @rightEar)";
|
||||||
|
cmd.Prepare();
|
||||||
|
|
||||||
|
cmd.Parameters.AddWithValue("@characterId", cid);
|
||||||
|
cmd.Parameters.AddWithValue("@baseId", 0xFFFFFFFF);
|
||||||
|
cmd.Parameters.AddWithValue("@size", charaInfo.appearance.size);
|
||||||
|
cmd.Parameters.AddWithValue("@voice", charaInfo.appearance.voice);
|
||||||
|
cmd.Parameters.AddWithValue("@skinColor", charaInfo.appearance.skinColor);
|
||||||
|
cmd.Parameters.AddWithValue("@hairStyle", charaInfo.appearance.hairStyle);
|
||||||
|
cmd.Parameters.AddWithValue("@hairColor", charaInfo.appearance.hairColor);
|
||||||
|
cmd.Parameters.AddWithValue("@hairHighlightColor", charaInfo.appearance.hairHighlightColor);
|
||||||
|
cmd.Parameters.AddWithValue("@eyeColor", charaInfo.appearance.eyeColor);
|
||||||
|
cmd.Parameters.AddWithValue("@faceType", charaInfo.appearance.faceType);
|
||||||
|
cmd.Parameters.AddWithValue("@faceEyebrows", charaInfo.appearance.faceEyebrows);
|
||||||
|
cmd.Parameters.AddWithValue("@faceEyeShape", charaInfo.appearance.faceEyeShape);
|
||||||
|
cmd.Parameters.AddWithValue("@faceIrisSize", charaInfo.appearance.faceIrisSize);
|
||||||
|
cmd.Parameters.AddWithValue("@faceNose", charaInfo.appearance.faceNose);
|
||||||
|
cmd.Parameters.AddWithValue("@faceMouth", charaInfo.appearance.faceMouth);
|
||||||
|
cmd.Parameters.AddWithValue("@faceFeatures", charaInfo.appearance.faceFeatures);
|
||||||
|
cmd.Parameters.AddWithValue("@characteristics", charaInfo.appearance.characteristics);
|
||||||
|
cmd.Parameters.AddWithValue("@characteristicsColor", charaInfo.appearance.characteristicsColor);
|
||||||
|
|
||||||
|
cmd.Parameters.AddWithValue("@mainhand", charaInfo.appearance.mainHand);
|
||||||
|
cmd.Parameters.AddWithValue("@offhand", charaInfo.appearance.offHand);
|
||||||
|
cmd.Parameters.AddWithValue("@head", charaInfo.appearance.head);
|
||||||
|
cmd.Parameters.AddWithValue("@body", charaInfo.appearance.body);
|
||||||
|
cmd.Parameters.AddWithValue("@hands", charaInfo.appearance.hands);
|
||||||
|
cmd.Parameters.AddWithValue("@legs", charaInfo.appearance.legs);
|
||||||
|
cmd.Parameters.AddWithValue("@feet", charaInfo.appearance.feet);
|
||||||
|
cmd.Parameters.AddWithValue("@waist", charaInfo.appearance.waist);
|
||||||
|
cmd.Parameters.AddWithValue("@leftFinger", charaInfo.appearance.leftFinger);
|
||||||
|
cmd.Parameters.AddWithValue("@rightFinger", charaInfo.appearance.rightFinger);
|
||||||
|
cmd.Parameters.AddWithValue("@leftEar", charaInfo.appearance.leftEar);
|
||||||
|
cmd.Parameters.AddWithValue("@rightEar", charaInfo.appearance.rightEar);
|
||||||
|
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.database(String.Format("CID={0} state updated to active(2).", cid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool renameCharacter(uint userId, uint characterId, uint serverId, 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();
|
||||||
|
|
||||||
|
//Check if exists
|
||||||
|
MySqlCommand cmd = new MySqlCommand("SELECT * FROM characters WHERE name=@name AND serverId=@serverId", conn);
|
||||||
|
cmd.Parameters.AddWithValue("@serverId", serverId);
|
||||||
|
cmd.Parameters.AddWithValue("@name", newName);
|
||||||
|
using (MySqlDataReader Reader = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
if (Reader.HasRows)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = new MySqlCommand();
|
||||||
|
cmd.Connection = conn;
|
||||||
|
cmd.CommandText = "UPDATE characters SET name=@name, doRename=0 WHERE id=@cid AND userId=@uid";
|
||||||
|
cmd.Prepare();
|
||||||
|
cmd.Parameters.AddWithValue("@uid", userId);
|
||||||
|
cmd.Parameters.AddWithValue("@cid", characterId);
|
||||||
|
cmd.Parameters.AddWithValue("@name", newName);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.database(String.Format("CID={0} name updated to \"{1}\".", characterId, newName));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 characters 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.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.database(String.Format("CID={0} deleted.", characterId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<World> getServers()
|
||||||
|
{
|
||||||
|
using (var 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 = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
worldList = conn.Query<World>("SELECT * FROM servers WHERE isActive=true").ToList();
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{ worldList = new List<World>(); }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
return worldList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static World getServer(uint serverId)
|
||||||
|
{
|
||||||
|
using (var 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();
|
||||||
|
world = conn.Query<World>("SELECT * FROM servers WHERE id=@ServerId", new {ServerId = serverId}).SingleOrDefault();
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Character> getCharacters(uint userId)
|
||||||
|
{
|
||||||
|
using (var 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<Character> charaList = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
charaList = conn.Query<Character>("SELECT * FROM characters WHERE userId=@UserId AND state = 2 ORDER BY slot", new { UserId = userId }).ToList();
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{ charaList = new List<Character>(); }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
return charaList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Character getCharacter(uint userId, uint charId)
|
||||||
|
{
|
||||||
|
using (var 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)))
|
||||||
|
{
|
||||||
|
Character chara = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
chara = conn.Query<Character>("SELECT * FROM characters WHERE id=@CharaId and userId=@UserId", new { UserId = userId, CharaId = charId }).SingleOrDefault();
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return chara;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Appearance getAppearance(uint charaId)
|
||||||
|
{
|
||||||
|
using (var 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)))
|
||||||
|
{
|
||||||
|
Appearance appearance = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
appearance = conn.Query<Appearance>("SELECT * FROM characters_appearance WHERE characterId=@CharaId", new { CharaId = charaId }).SingleOrDefault();
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return appearance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getReservedNames(uint userId)
|
||||||
|
{
|
||||||
|
using (var 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<String> nameList = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
nameList = conn.Query<String>("SELECT name FROM reserved_names WHERE userId=@UserId", new { UserId = userId }).ToList();
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{ nameList = new List<String>(); }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
return nameList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Retainer> getRetainers(uint userId)
|
||||||
|
{
|
||||||
|
using (var 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<Retainer> retainerList = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
retainerList = conn.Query<Retainer>("SELECT * FROM retainers WHERE id=@UserId ORDER BY characterId, slot", new { UserId = userId }).ToList();
|
||||||
|
}
|
||||||
|
catch (MySqlException e)
|
||||||
|
{ retainerList = new List<Retainer>(); }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
conn.Dispose();
|
||||||
|
}
|
||||||
|
return retainerList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
104
FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj
Normal file
104
FFXIVClassic_Lobby_Server/FFXIVClassic_Lobby_Server.csproj
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?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="Dapper">
|
||||||
|
<HintPath>..\packages\Dapper.1.42\lib\net45\Dapper.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="MySql.Data">
|
||||||
|
<HintPath>..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json">
|
||||||
|
<HintPath>..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.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="common\Bitfield.cs" />
|
||||||
|
<Compile Include="common\Blowfish.cs" />
|
||||||
|
<Compile Include="common\Log.cs" />
|
||||||
|
<Compile Include="common\STA_INIFile.cs" />
|
||||||
|
<Compile Include="dataobjects\Account.cs" />
|
||||||
|
<Compile Include="dataobjects\Appearance.cs" />
|
||||||
|
<Compile Include="dataobjects\CharaInfo.cs" />
|
||||||
|
<Compile Include="dataobjects\Retainer.cs" />
|
||||||
|
<Compile Include="dataobjects\Character.cs" />
|
||||||
|
<Compile Include="ClientConnection.cs" />
|
||||||
|
<Compile Include="common\Utils.cs" />
|
||||||
|
<Compile Include="ConfigConstants.cs" />
|
||||||
|
<Compile Include="Database.cs" />
|
||||||
|
<Compile Include="dataobjects\World.cs" />
|
||||||
|
<Compile Include="PacketProcessor.cs" />
|
||||||
|
<Compile Include="packets\BasePacket.cs" />
|
||||||
|
<Compile Include="packets\receive\CharacterModifyPacket.cs" />
|
||||||
|
<Compile Include="packets\receive\SecurityHandshakePacket.cs" />
|
||||||
|
<Compile Include="packets\receive\SelectCharacterPacket.cs" />
|
||||||
|
<Compile Include="packets\receive\SessionPacket.cs" />
|
||||||
|
<Compile Include="packets\send\CharaCreatorPacket.cs" />
|
||||||
|
<Compile Include="packets\send\RetainerListPacket.cs" />
|
||||||
|
<Compile Include="packets\send\ErrorPacket.cs" />
|
||||||
|
<Compile Include="packets\HardCoded_Packets.cs" />
|
||||||
|
<Compile Include="packets\send\SelectCharacterConfirmPacket.cs" />
|
||||||
|
<Compile Include="packets\SubPacket.cs" />
|
||||||
|
<Compile Include="packets\send\CharacterListPacket.cs" />
|
||||||
|
<Compile Include="packets\send\ImportListPacket.cs" />
|
||||||
|
<Compile Include="packets\send\AccountListPacket.cs" />
|
||||||
|
<Compile Include="packets\send\WorldListPacket.cs" />
|
||||||
|
<Compile Include="Program.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="Server.cs" />
|
||||||
|
<Compile Include="utils\CharacterCreatorUtils.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>
|
389
FFXIVClassic_Lobby_Server/PacketProcessor.cs
Normal file
389
FFXIVClassic_Lobby_Server/PacketProcessor.cs
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
using FFXIVClassic_Lobby_Server.common;
|
||||||
|
using FFXIVClassic_Lobby_Server.dataobjects;
|
||||||
|
using FFXIVClassic_Lobby_Server.packets;
|
||||||
|
using FFXIVClassic_Lobby_Server.packets.receive;
|
||||||
|
using FFXIVClassic_Lobby_Server.utils;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server
|
||||||
|
{
|
||||||
|
class PacketProcessor
|
||||||
|
{
|
||||||
|
|
||||||
|
public void processPacket(ClientConnection client, BasePacket packet)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ((packet.header.packetSize == 0x288) && (packet.data[0x34] == 'T')) //Test Ticket Data
|
||||||
|
{
|
||||||
|
packet.debugPrintPacket();
|
||||||
|
//Crypto handshake
|
||||||
|
ProcessStartSession(client, packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BasePacket.decryptPacket(client.blowfish, ref packet);
|
||||||
|
|
||||||
|
packet.debugPrintPacket();
|
||||||
|
|
||||||
|
List<SubPacket> subPackets = packet.getSubpackets();
|
||||||
|
foreach (SubPacket subpacket in subPackets)
|
||||||
|
{
|
||||||
|
subpacket.debugPrintSubPacket();
|
||||||
|
|
||||||
|
if (subpacket.header.type == 3)
|
||||||
|
{
|
||||||
|
switch (subpacket.gameMessage.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;
|
||||||
|
case 0x0F:
|
||||||
|
//Mod Retainers
|
||||||
|
default:
|
||||||
|
Log.debug(String.Format("Unknown command 0x{0:X} received.", subpacket.gameMessage.opcode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessStartSession(ClientConnection client, BasePacket packet)
|
||||||
|
{
|
||||||
|
SecurityHandshakePacket securityHandshake = new SecurityHandshakePacket(packet.data);
|
||||||
|
|
||||||
|
byte[] blowfishKey = GenerateKey(securityHandshake.ticketPhrase, securityHandshake.clientNumber);
|
||||||
|
client.blowfish = new Blowfish(blowfishKey);
|
||||||
|
|
||||||
|
Log.info(String.Format("SecCNum: 0x{0:X}", securityHandshake.clientNumber));
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
packet.debugPrintSubPacket();
|
||||||
|
SessionPacket sessionPacket = new SessionPacket(packet.data);
|
||||||
|
String clientVersion = sessionPacket.version;
|
||||||
|
|
||||||
|
Log.info(String.Format("Got acknowledgment for secure session."));
|
||||||
|
Log.info(String.Format("CLIENT VERSION: {0}", clientVersion));
|
||||||
|
|
||||||
|
uint userId = Database.getUserIdFromSession(sessionPacket.session);
|
||||||
|
client.currentUserId = userId;
|
||||||
|
client.currentSessionToken = sessionPacket.session; ;
|
||||||
|
|
||||||
|
if (userId == 0)
|
||||||
|
{
|
||||||
|
ErrorPacket errorPacket = new ErrorPacket(sessionPacket.sequence, 0, 0, 13001, "Your session has expired, please login again.");
|
||||||
|
SubPacket subpacket = errorPacket.buildPacket();
|
||||||
|
BasePacket errorBasePacket = BasePacket.createPacket(subpacket, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, errorBasePacket);
|
||||||
|
client.queuePacket(errorBasePacket);
|
||||||
|
|
||||||
|
Log.info(String.Format("Invalid session, kicking..."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info(String.Format("USER ID: {0}", userId));
|
||||||
|
|
||||||
|
List<Account> accountList = new List<Account>();
|
||||||
|
Account defaultAccount = new Account();
|
||||||
|
defaultAccount.id = 1;
|
||||||
|
defaultAccount.name = "FINAL FANTASY XIV";
|
||||||
|
accountList.Add(defaultAccount);
|
||||||
|
AccountListPacket listPacket = new AccountListPacket(1, accountList);
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(listPacket.buildPackets(), true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessGetCharacters(ClientConnection client, SubPacket packet)
|
||||||
|
{
|
||||||
|
Log.info(String.Format("{0} => Get characters", client.currentUserId == 0 ? client.getAddress() : "User " + client.currentUserId));
|
||||||
|
|
||||||
|
sendWorldList(client, packet);
|
||||||
|
sendImportList(client, packet);
|
||||||
|
sendRetainerList(client, packet);
|
||||||
|
sendCharacterList(client, packet);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessSelectCharacter(ClientConnection client, SubPacket packet)
|
||||||
|
{
|
||||||
|
SelectCharacterPacket selectCharRequest = new SelectCharacterPacket(packet.data);
|
||||||
|
|
||||||
|
Log.info(String.Format("{0} => Select character id {1}", client.currentUserId == 0 ? client.getAddress() : "User " + client.currentUserId, selectCharRequest.characterId));
|
||||||
|
|
||||||
|
Character chara = Database.getCharacter(client.currentUserId, selectCharRequest.characterId);
|
||||||
|
World world = null;
|
||||||
|
|
||||||
|
if (chara != null)
|
||||||
|
world = Database.getServer(chara.serverId);
|
||||||
|
|
||||||
|
if (world == null)
|
||||||
|
{
|
||||||
|
ErrorPacket errorPacket = new ErrorPacket(selectCharRequest.sequence, 0, 0, 13001, "World does not exist or is inactive.");
|
||||||
|
SubPacket subpacket = errorPacket.buildPacket();
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(subpacket, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectCharacterConfirmPacket connectCharacter = new SelectCharacterConfirmPacket(selectCharRequest.sequence, selectCharRequest.characterId, client.currentSessionToken, world.address, world.port, selectCharRequest.ticket);
|
||||||
|
|
||||||
|
BasePacket outgoingPacket = BasePacket.createPacket(connectCharacter.buildPackets(), true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, outgoingPacket);
|
||||||
|
client.queuePacket(outgoingPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessModifyCharacter(ClientConnection client, SubPacket packet)
|
||||||
|
{
|
||||||
|
CharacterModifyPacket charaReq = new CharacterModifyPacket(packet.data);
|
||||||
|
var slot = charaReq.slot;
|
||||||
|
var name = charaReq.characterName;
|
||||||
|
var worldId = charaReq.worldId;
|
||||||
|
|
||||||
|
uint pid = 0, cid = 0;
|
||||||
|
|
||||||
|
//Get world from new char instance
|
||||||
|
if (worldId == 0)
|
||||||
|
worldId = client.newCharaWorldId;
|
||||||
|
|
||||||
|
//Check if this character exists, get world from there
|
||||||
|
if (worldId == 0 && charaReq.characterId != 0)
|
||||||
|
{
|
||||||
|
Character chara = Database.getCharacter(client.currentUserId, charaReq.characterId);
|
||||||
|
if (chara != null)
|
||||||
|
worldId = chara.serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
string worldName = null;
|
||||||
|
World world = Database.getServer(worldId);
|
||||||
|
if (world != null)
|
||||||
|
worldName = world.name;
|
||||||
|
|
||||||
|
if (worldName == null)
|
||||||
|
{
|
||||||
|
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 0, 0, 13001, "World does not exist or is inactive.");
|
||||||
|
SubPacket subpacket = errorPacket.buildPacket();
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(subpacket, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
|
||||||
|
Log.info(String.Format("User {0} => Error; invalid server id: \"{1}\"", client.currentUserId, worldId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool alreadyTaken;
|
||||||
|
|
||||||
|
switch (charaReq.command)
|
||||||
|
{
|
||||||
|
case 0x01://Reserve
|
||||||
|
|
||||||
|
alreadyTaken = Database.reserveCharacter(client.currentUserId, slot, worldId, name, out pid, out cid);
|
||||||
|
|
||||||
|
if (alreadyTaken)
|
||||||
|
{
|
||||||
|
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 1003, 0, 13005, ""); //BDB - Chara Name Used, //1003 - Bad Word
|
||||||
|
SubPacket subpacket = errorPacket.buildPacket();
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(subpacket, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
|
||||||
|
Log.info(String.Format("User {0} => Error; name taken: \"{1}\"", client.currentUserId, charaReq.characterName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pid = 0;
|
||||||
|
client.newCharaCid = cid;
|
||||||
|
client.newCharaSlot = slot;
|
||||||
|
client.newCharaWorldId = worldId;
|
||||||
|
client.newCharaName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info(String.Format("User {0} => Character reserved \"{1}\"", client.currentUserId, name));
|
||||||
|
break;
|
||||||
|
case 0x02://Make
|
||||||
|
CharaInfo info = CharaInfo.getFromNewCharRequest(charaReq.characterInfoEncoded);
|
||||||
|
|
||||||
|
//Set Initial Appearance (items will be loaded in by map server)
|
||||||
|
uint[] classAppearance = CharacterCreatorUtils.getEquipmentForClass(info.currentClass);
|
||||||
|
info.weapon1 = classAppearance[0];
|
||||||
|
info.weapon2 = classAppearance[1];
|
||||||
|
info.head = classAppearance[7];
|
||||||
|
info.body = classAppearance[8];
|
||||||
|
info.legs = classAppearance[9];
|
||||||
|
info.hands = classAppearance[10];
|
||||||
|
info.feet = classAppearance[11];
|
||||||
|
info.belt = classAppearance[12];
|
||||||
|
|
||||||
|
//Set Initial Position
|
||||||
|
switch (info.initialTown)
|
||||||
|
{
|
||||||
|
case 1: //ocn0Battle02 (Limsa)
|
||||||
|
info.zoneId = 193;
|
||||||
|
info.x = 0.016f;
|
||||||
|
info.y = 10.35f;
|
||||||
|
info.z = -36.91f;
|
||||||
|
info.rot = 0.025f;
|
||||||
|
break;
|
||||||
|
case 2: //fst0Battle03 (Gridania)
|
||||||
|
info.zoneId = 166;
|
||||||
|
info.x = 356.09f;
|
||||||
|
info.y = 3.74f;
|
||||||
|
info.z = -701.62f;
|
||||||
|
info.rot = -1.4f;
|
||||||
|
break;
|
||||||
|
case 3: //wil0Battle01 (Ul'dah)
|
||||||
|
info.zoneId = 184;
|
||||||
|
info.x = 12.63f;
|
||||||
|
info.y = 196.05f;
|
||||||
|
info.z = 131.01f;
|
||||||
|
info.rot = -1.34f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database.makeCharacter(client.currentUserId, client.newCharaCid, info);
|
||||||
|
|
||||||
|
pid = 1;
|
||||||
|
cid = client.newCharaCid;
|
||||||
|
name = client.newCharaName;
|
||||||
|
|
||||||
|
Log.info(String.Format("User {0} => Character created \"{1}\"", client.currentUserId, name));
|
||||||
|
break;
|
||||||
|
case 0x03://Rename
|
||||||
|
|
||||||
|
alreadyTaken = Database.renameCharacter(client.currentUserId, charaReq.characterId, worldId, charaReq.characterName);
|
||||||
|
|
||||||
|
if (alreadyTaken)
|
||||||
|
{
|
||||||
|
ErrorPacket errorPacket = new ErrorPacket(charaReq.sequence, 1003, 0, 13005, ""); //BDB - Chara Name Used, //1003 - Bad Word
|
||||||
|
SubPacket subpacket = errorPacket.buildPacket();
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(subpacket, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
|
||||||
|
Log.info(String.Format("User {0} => Error; name taken: \"{1}\"", client.currentUserId, charaReq.characterName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info(String.Format("User {0} => Character renamed \"{1}\"", client.currentUserId, name));
|
||||||
|
break;
|
||||||
|
case 0x04://Delete
|
||||||
|
Database.deleteCharacter(charaReq.characterId, charaReq.characterName);
|
||||||
|
|
||||||
|
Log.info(String.Format("User {0} => Character deleted \"{1}\"", client.currentUserId, name));
|
||||||
|
break;
|
||||||
|
case 0x06://Rename Retainer
|
||||||
|
|
||||||
|
Log.info(String.Format("User {0} => Retainer renamed \"{1}\"", client.currentUserId, name));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharaCreatorPacket charaCreator = new CharaCreatorPacket(charaReq.sequence, charaReq.command, pid, cid, 1, name, worldName);
|
||||||
|
BasePacket charaCreatorPacket = BasePacket.createPacket(charaCreator.buildPacket(), true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, charaCreatorPacket);
|
||||||
|
client.queuePacket(charaCreatorPacket);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendWorldList(ClientConnection client, SubPacket packet)
|
||||||
|
{
|
||||||
|
List<World> serverList = Database.getServers();
|
||||||
|
WorldListPacket worldlistPacket = new WorldListPacket(0, serverList);
|
||||||
|
List<SubPacket> subPackets = worldlistPacket.buildPackets();
|
||||||
|
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(subPackets, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendImportList(ClientConnection client, SubPacket packet)
|
||||||
|
{
|
||||||
|
List<String> names = Database.getReservedNames(client.currentUserId);
|
||||||
|
|
||||||
|
ImportListPacket importListPacket = new ImportListPacket(0, names);
|
||||||
|
List<SubPacket> subPackets = importListPacket.buildPackets();
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(subPackets, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendRetainerList(ClientConnection client, SubPacket packet)
|
||||||
|
{
|
||||||
|
List<Retainer> retainers = Database.getRetainers(client.currentUserId);
|
||||||
|
|
||||||
|
RetainerListPacket retainerListPacket = new RetainerListPacket(0, retainers);
|
||||||
|
List<SubPacket> subPackets = retainerListPacket.buildPackets();
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(subPackets, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendCharacterList(ClientConnection client, SubPacket packet)
|
||||||
|
{
|
||||||
|
List<Character> characterList = Database.getCharacters(client.currentUserId);
|
||||||
|
|
||||||
|
if (characterList.Count > 8)
|
||||||
|
Log.error("Warning, got more than 8 characters. List truncated, check DB for issues.");
|
||||||
|
|
||||||
|
CharacterListPacket characterlistPacket = new CharacterListPacket(0, characterList);
|
||||||
|
List<SubPacket> subPackets = characterlistPacket.buildPackets();
|
||||||
|
BasePacket basePacket = BasePacket.createPacket(subPackets, true, false);
|
||||||
|
BasePacket.encryptPacket(client.blowfish, basePacket);
|
||||||
|
client.queuePacket(basePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] GenerateKey(string ticketPhrase, uint clientNumber)
|
||||||
|
{
|
||||||
|
byte[] key;
|
||||||
|
using (MemoryStream memStream = new MemoryStream(0x2C))
|
||||||
|
{
|
||||||
|
using (BinaryWriter binWriter = new BinaryWriter(memStream))
|
||||||
|
{
|
||||||
|
binWriter.Write((Byte)0x78);
|
||||||
|
binWriter.Write((Byte)0x56);
|
||||||
|
binWriter.Write((Byte)0x34);
|
||||||
|
binWriter.Write((Byte)0x12);
|
||||||
|
binWriter.Write((UInt32)clientNumber);
|
||||||
|
binWriter.Write((Byte)0xE8);
|
||||||
|
binWriter.Write((Byte)0x03);
|
||||||
|
binWriter.Write((Byte)0x00);
|
||||||
|
binWriter.Write((Byte)0x00);
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(ticketPhrase), 0, Encoding.ASCII.GetByteCount(ticketPhrase) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(ticketPhrase));
|
||||||
|
}
|
||||||
|
byte[] nonMD5edKey = memStream.GetBuffer();
|
||||||
|
|
||||||
|
using (MD5 md5Hash = MD5.Create())
|
||||||
|
{
|
||||||
|
key = md5Hash.ComputeHash(nonMD5edKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
FFXIVClassic_Lobby_Server/Program.cs
Normal file
73
FFXIVClassic_Lobby_Server/Program.cs
Normal 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 to \"{0}\"... ", ConfigConstants.DATABASE_HOST);
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
36
FFXIVClassic_Lobby_Server/Properties/AssemblyInfo.cs
Normal file
36
FFXIVClassic_Lobby_Server/Properties/AssemblyInfo.cs
Normal 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")]
|
246
FFXIVClassic_Lobby_Server/Server.cs
Normal file
246
FFXIVClassic_Lobby_Server/Server.cs
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
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;
|
||||||
|
using FFXIVClassic_Lobby_Server.common;
|
||||||
|
using FFXIVClassic_Lobby_Server.packets;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server
|
||||||
|
{
|
||||||
|
class Server
|
||||||
|
{
|
||||||
|
public const int FFXIV_LOBBY_PORT = 54994;
|
||||||
|
public const int BUFFER_SIZE = 0xFFFF;
|
||||||
|
public const int BACKLOG = 100;
|
||||||
|
|
||||||
|
private const int CLEANUP_THREAD_SLEEP_TIME = 60;
|
||||||
|
|
||||||
|
private Socket mServerSocket;
|
||||||
|
private List<ClientConnection> mConnectionList = new List<ClientConnection>();
|
||||||
|
private PacketProcessor mProcessor;
|
||||||
|
|
||||||
|
private Thread cleanupThread;
|
||||||
|
private bool killCleanupThread = false;
|
||||||
|
|
||||||
|
private void socketCleanup()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Cleanup thread started; it will run every {0} seconds.", CLEANUP_THREAD_SLEEP_TIME);
|
||||||
|
while (!killCleanupThread)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (int i = mConnectionList.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
ClientConnection conn = mConnectionList[i];
|
||||||
|
if (conn.socket.Poll(1, SelectMode.SelectRead) && conn.socket.Available == 0)
|
||||||
|
{
|
||||||
|
conn.socket.Disconnect(false);
|
||||||
|
mConnectionList.Remove(conn);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count != 0)
|
||||||
|
Log.conn(String.Format("{0} connections were cleaned up.", count));
|
||||||
|
Thread.Sleep(CLEANUP_THREAD_SLEEP_TIME*1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Socket Handling
|
||||||
|
public bool startServer()
|
||||||
|
{
|
||||||
|
cleanupThread = new Thread(new ThreadStart(socketCleanup));
|
||||||
|
cleanupThread.Name = "LobbyThread:Cleanup";
|
||||||
|
cleanupThread.Start();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
|
||||||
|
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);
|
||||||
|
Log.conn(String.Format("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);
|
||||||
|
|
||||||
|
bytesRead += conn.lastPartialSize;
|
||||||
|
|
||||||
|
if (bytesRead >= 0)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
//Build packets until can no longer or out of data
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
BasePacket basePacket = buildPacket(ref offset, conn.buffer, bytesRead);
|
||||||
|
|
||||||
|
//If can't build packet, break, else process another
|
||||||
|
if (basePacket == null)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
mProcessor.processPacket(conn, basePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Not all bytes consumed, transfer leftover to beginning
|
||||||
|
if (offset < bytesRead)
|
||||||
|
Array.Copy(conn.buffer, offset, conn.buffer, 0, bytesRead - offset);
|
||||||
|
|
||||||
|
Array.Clear(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset));
|
||||||
|
|
||||||
|
conn.lastPartialSize = bytesRead - offset;
|
||||||
|
|
||||||
|
//Build any queued subpackets into basepackets and send
|
||||||
|
conn.flushQueuedSendPackets();
|
||||||
|
|
||||||
|
if (offset < bytesRead)
|
||||||
|
//Need offset since not all bytes consumed
|
||||||
|
conn.socket.BeginReceive(conn.buffer, bytesRead - offset, conn.buffer.Length - (bytesRead - offset), SocketFlags.None, new AsyncCallback(receiveCallback), conn);
|
||||||
|
else
|
||||||
|
//All bytes consumed, full buffer available
|
||||||
|
conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), conn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.conn(String.Format("{0} has disconnected.", conn.currentUserId == 0 ? conn.getAddress() : "User " + conn.currentUserId));
|
||||||
|
|
||||||
|
lock (mConnectionList)
|
||||||
|
{
|
||||||
|
mConnectionList.Remove(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (SocketException)
|
||||||
|
{
|
||||||
|
if (conn.socket != null)
|
||||||
|
{
|
||||||
|
Log.conn(String.Format("{0} has disconnected.", conn.currentUserId == 0 ? conn.getAddress() : "User " + conn.currentUserId));
|
||||||
|
|
||||||
|
lock (mConnectionList)
|
||||||
|
{
|
||||||
|
mConnectionList.Remove(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a packet from the incoming buffer + offset. If a packet can be built, it is returned else null.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="offset">Current offset in buffer.</param>
|
||||||
|
/// <param name="buffer">Incoming buffer.</param>
|
||||||
|
/// <returns>Returns either a BasePacket or null if not enough data.</returns>
|
||||||
|
public BasePacket buildPacket(ref int offset, byte[] buffer, int bytesRead)
|
||||||
|
{
|
||||||
|
BasePacket newPacket = null;
|
||||||
|
|
||||||
|
//Too small to even get length
|
||||||
|
if (bytesRead <= offset)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
ushort packetSize = BitConverter.ToUInt16(buffer, offset);
|
||||||
|
|
||||||
|
//Too small to whole packet
|
||||||
|
if (bytesRead < offset + packetSize)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (buffer.Length < offset + packetSize)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
newPacket = new BasePacket(buffer, ref offset);
|
||||||
|
}
|
||||||
|
catch (OverflowException)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
9
FFXIVClassic_Lobby_Server/app.config
Normal file
9
FFXIVClassic_Lobby_Server/app.config
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<system.data>
|
||||||
|
<DbProviderFactories>
|
||||||
|
<remove invariant="MySql.Data.MySqlClient" />
|
||||||
|
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
|
||||||
|
</DbProviderFactories>
|
||||||
|
</system.data>
|
||||||
|
</configuration>
|
78
FFXIVClassic_Lobby_Server/common/Bitfield.cs
Normal file
78
FFXIVClassic_Lobby_Server/common/Bitfield.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.common
|
||||||
|
{
|
||||||
|
[global::System.AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||||
|
sealed class BitfieldLengthAttribute : Attribute
|
||||||
|
{
|
||||||
|
uint length;
|
||||||
|
|
||||||
|
public BitfieldLengthAttribute(uint length)
|
||||||
|
{
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint Length { get { return length; } }
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PrimitiveConversion
|
||||||
|
{
|
||||||
|
public static UInt32 ToUInt32<T>(T t) where T : struct
|
||||||
|
{
|
||||||
|
UInt32 r = 0;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
// For every field suitably attributed with a BitfieldLength
|
||||||
|
foreach (System.Reflection.FieldInfo f in t.GetType().GetFields())
|
||||||
|
{
|
||||||
|
object[] attrs = f.GetCustomAttributes(typeof(BitfieldLengthAttribute), false);
|
||||||
|
if (attrs.Length == 1)
|
||||||
|
{
|
||||||
|
uint fieldLength = ((BitfieldLengthAttribute)attrs[0]).Length;
|
||||||
|
|
||||||
|
// Calculate a bitmask of the desired length
|
||||||
|
uint mask = 0;
|
||||||
|
for (int i = 0; i < fieldLength; i++)
|
||||||
|
mask |= (UInt32)1 << i;
|
||||||
|
|
||||||
|
r |= ((UInt32)f.GetValue(t) & mask) << offset;
|
||||||
|
|
||||||
|
offset += (int)fieldLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long ToLong<T>(T t) where T : struct
|
||||||
|
{
|
||||||
|
long r = 0;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
// For every field suitably attributed with a BitfieldLength
|
||||||
|
foreach (System.Reflection.FieldInfo f in t.GetType().GetFields())
|
||||||
|
{
|
||||||
|
object[] attrs = f.GetCustomAttributes(typeof(BitfieldLengthAttribute), false);
|
||||||
|
if (attrs.Length == 1)
|
||||||
|
{
|
||||||
|
uint fieldLength = ((BitfieldLengthAttribute)attrs[0]).Length;
|
||||||
|
|
||||||
|
// Calculate a bitmask of the desired length
|
||||||
|
long mask = 0;
|
||||||
|
for (int i = 0; i < fieldLength; i++)
|
||||||
|
mask |= 1 << i;
|
||||||
|
|
||||||
|
r |= ((UInt32)f.GetValue(t) & mask) << offset;
|
||||||
|
|
||||||
|
offset += (int)fieldLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
460
FFXIVClassic_Lobby_Server/common/Blowfish.cs
Normal file
460
FFXIVClassic_Lobby_Server/common/Blowfish.cs
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.common
|
||||||
|
{
|
||||||
|
public class Blowfish
|
||||||
|
{
|
||||||
|
const int N = 16;
|
||||||
|
UInt32 [] P = new uint[16 + 2];
|
||||||
|
UInt32 [,] S = new UInt32[4,256];
|
||||||
|
|
||||||
|
#region P and S Values
|
||||||
|
|
||||||
|
byte [] P_values =
|
||||||
|
{
|
||||||
|
0x88, 0x6A, 0x3F, 0x24, 0xD3, 0x08, 0xA3, 0x85, 0x2E, 0x8A, 0x19, 0x13, 0x44, 0x73, 0x70, 0x03,
|
||||||
|
0x22, 0x38, 0x09, 0xA4, 0xD0, 0x31, 0x9F, 0x29, 0x98, 0xFA, 0x2E, 0x08, 0x89, 0x6C, 0x4E, 0xEC,
|
||||||
|
0xE6, 0x21, 0x28, 0x45, 0x77, 0x13, 0xD0, 0x38, 0xCF, 0x66, 0x54, 0xBE, 0x6C, 0x0C, 0xE9, 0x34,
|
||||||
|
0xB7, 0x29, 0xAC, 0xC0, 0xDD, 0x50, 0x7C, 0xC9, 0xB5, 0xD5, 0x84, 0x3F, 0x17, 0x09, 0x47, 0xB5,
|
||||||
|
0xD9, 0xD5, 0x16, 0x92, 0x1B, 0xFB, 0x79, 0x89
|
||||||
|
};
|
||||||
|
|
||||||
|
byte [] S_values =
|
||||||
|
{
|
||||||
|
0xA6, 0x0B, 0x31, 0xD1, 0xAC, 0xB5, 0xDF, 0x98, 0xDB, 0x72, 0xFD, 0x2F, 0xB7, 0xDF, 0x1A, 0xD0,
|
||||||
|
0xED, 0xAF, 0xE1, 0xB8, 0x96, 0x7E, 0x26, 0x6A, 0x45, 0x90, 0x7C, 0xBA, 0x99, 0x7F, 0x2C, 0xF1,
|
||||||
|
0x47, 0x99, 0xA1, 0x24, 0xF7, 0x6C, 0x91, 0xB3, 0xE2, 0xF2, 0x01, 0x08, 0x16, 0xFC, 0x8E, 0x85,
|
||||||
|
0xD8, 0x20, 0x69, 0x63, 0x69, 0x4E, 0x57, 0x71, 0xA3, 0xFE, 0x58, 0xA4, 0x7E, 0x3D, 0x93, 0xF4,
|
||||||
|
0x8F, 0x74, 0x95, 0x0D, 0x58, 0xB6, 0x8E, 0x72, 0x58, 0xCD, 0x8B, 0x71, 0xEE, 0x4A, 0x15, 0x82,
|
||||||
|
0x1D, 0xA4, 0x54, 0x7B, 0xB5, 0x59, 0x5A, 0xC2, 0x39, 0xD5, 0x30, 0x9C, 0x13, 0x60, 0xF2, 0x2A,
|
||||||
|
0x23, 0xB0, 0xD1, 0xC5, 0xF0, 0x85, 0x60, 0x28, 0x18, 0x79, 0x41, 0xCA, 0xEF, 0x38, 0xDB, 0xB8,
|
||||||
|
0xB0, 0xDC, 0x79, 0x8E, 0x0E, 0x18, 0x3A, 0x60, 0x8B, 0x0E, 0x9E, 0x6C, 0x3E, 0x8A, 0x1E, 0xB0,
|
||||||
|
0xC1, 0x77, 0x15, 0xD7, 0x27, 0x4B, 0x31, 0xBD, 0xDA, 0x2F, 0xAF, 0x78, 0x60, 0x5C, 0x60, 0x55,
|
||||||
|
0xF3, 0x25, 0x55, 0xE6, 0x94, 0xAB, 0x55, 0xAA, 0x62, 0x98, 0x48, 0x57, 0x40, 0x14, 0xE8, 0x63,
|
||||||
|
0x6A, 0x39, 0xCA, 0x55, 0xB6, 0x10, 0xAB, 0x2A, 0x34, 0x5C, 0xCC, 0xB4, 0xCE, 0xE8, 0x41, 0x11,
|
||||||
|
0xAF, 0x86, 0x54, 0xA1, 0x93, 0xE9, 0x72, 0x7C, 0x11, 0x14, 0xEE, 0xB3, 0x2A, 0xBC, 0x6F, 0x63,
|
||||||
|
0x5D, 0xC5, 0xA9, 0x2B, 0xF6, 0x31, 0x18, 0x74, 0x16, 0x3E, 0x5C, 0xCE, 0x1E, 0x93, 0x87, 0x9B,
|
||||||
|
0x33, 0xBA, 0xD6, 0xAF, 0x5C, 0xCF, 0x24, 0x6C, 0x81, 0x53, 0x32, 0x7A, 0x77, 0x86, 0x95, 0x28,
|
||||||
|
0x98, 0x48, 0x8F, 0x3B, 0xAF, 0xB9, 0x4B, 0x6B, 0x1B, 0xE8, 0xBF, 0xC4, 0x93, 0x21, 0x28, 0x66,
|
||||||
|
0xCC, 0x09, 0xD8, 0x61, 0x91, 0xA9, 0x21, 0xFB, 0x60, 0xAC, 0x7C, 0x48, 0x32, 0x80, 0xEC, 0x5D,
|
||||||
|
0x5D, 0x5D, 0x84, 0xEF, 0xB1, 0x75, 0x85, 0xE9, 0x02, 0x23, 0x26, 0xDC, 0x88, 0x1B, 0x65, 0xEB,
|
||||||
|
0x81, 0x3E, 0x89, 0x23, 0xC5, 0xAC, 0x96, 0xD3, 0xF3, 0x6F, 0x6D, 0x0F, 0x39, 0x42, 0xF4, 0x83,
|
||||||
|
0x82, 0x44, 0x0B, 0x2E, 0x04, 0x20, 0x84, 0xA4, 0x4A, 0xF0, 0xC8, 0x69, 0x5E, 0x9B, 0x1F, 0x9E,
|
||||||
|
0x42, 0x68, 0xC6, 0x21, 0x9A, 0x6C, 0xE9, 0xF6, 0x61, 0x9C, 0x0C, 0x67, 0xF0, 0x88, 0xD3, 0xAB,
|
||||||
|
0xD2, 0xA0, 0x51, 0x6A, 0x68, 0x2F, 0x54, 0xD8, 0x28, 0xA7, 0x0F, 0x96, 0xA3, 0x33, 0x51, 0xAB,
|
||||||
|
0x6C, 0x0B, 0xEF, 0x6E, 0xE4, 0x3B, 0x7A, 0x13, 0x50, 0xF0, 0x3B, 0xBA, 0x98, 0x2A, 0xFB, 0x7E,
|
||||||
|
0x1D, 0x65, 0xF1, 0xA1, 0x76, 0x01, 0xAF, 0x39, 0x3E, 0x59, 0xCA, 0x66, 0x88, 0x0E, 0x43, 0x82,
|
||||||
|
0x19, 0x86, 0xEE, 0x8C, 0xB4, 0x9F, 0x6F, 0x45, 0xC3, 0xA5, 0x84, 0x7D, 0xBE, 0x5E, 0x8B, 0x3B,
|
||||||
|
0xD8, 0x75, 0x6F, 0xE0, 0x73, 0x20, 0xC1, 0x85, 0x9F, 0x44, 0x1A, 0x40, 0xA6, 0x6A, 0xC1, 0x56,
|
||||||
|
0x62, 0xAA, 0xD3, 0x4E, 0x06, 0x77, 0x3F, 0x36, 0x72, 0xDF, 0xFE, 0x1B, 0x3D, 0x02, 0x9B, 0x42,
|
||||||
|
0x24, 0xD7, 0xD0, 0x37, 0x48, 0x12, 0x0A, 0xD0, 0xD3, 0xEA, 0x0F, 0xDB, 0x9B, 0xC0, 0xF1, 0x49,
|
||||||
|
0xC9, 0x72, 0x53, 0x07, 0x7B, 0x1B, 0x99, 0x80, 0xD8, 0x79, 0xD4, 0x25, 0xF7, 0xDE, 0xE8, 0xF6,
|
||||||
|
0x1A, 0x50, 0xFE, 0xE3, 0x3B, 0x4C, 0x79, 0xB6, 0xBD, 0xE0, 0x6C, 0x97, 0xBA, 0x06, 0xC0, 0x04,
|
||||||
|
0xB6, 0x4F, 0xA9, 0xC1, 0xC4, 0x60, 0x9F, 0x40, 0xC2, 0x9E, 0x5C, 0x5E, 0x63, 0x24, 0x6A, 0x19,
|
||||||
|
0xAF, 0x6F, 0xFB, 0x68, 0xB5, 0x53, 0x6C, 0x3E, 0xEB, 0xB2, 0x39, 0x13, 0x6F, 0xEC, 0x52, 0x3B,
|
||||||
|
0x1F, 0x51, 0xFC, 0x6D, 0x2C, 0x95, 0x30, 0x9B, 0x44, 0x45, 0x81, 0xCC, 0x09, 0xBD, 0x5E, 0xAF,
|
||||||
|
0x04, 0xD0, 0xE3, 0xBE, 0xFD, 0x4A, 0x33, 0xDE, 0x07, 0x28, 0x0F, 0x66, 0xB3, 0x4B, 0x2E, 0x19,
|
||||||
|
0x57, 0xA8, 0xCB, 0xC0, 0x0F, 0x74, 0xC8, 0x45, 0x39, 0x5F, 0x0B, 0xD2, 0xDB, 0xFB, 0xD3, 0xB9,
|
||||||
|
0xBD, 0xC0, 0x79, 0x55, 0x0A, 0x32, 0x60, 0x1A, 0xC6, 0x00, 0xA1, 0xD6, 0x79, 0x72, 0x2C, 0x40,
|
||||||
|
0xFE, 0x25, 0x9F, 0x67, 0xCC, 0xA3, 0x1F, 0xFB, 0xF8, 0xE9, 0xA5, 0x8E, 0xF8, 0x22, 0x32, 0xDB,
|
||||||
|
0xDF, 0x16, 0x75, 0x3C, 0x15, 0x6B, 0x61, 0xFD, 0xC8, 0x1E, 0x50, 0x2F, 0xAB, 0x52, 0x05, 0xAD,
|
||||||
|
0xFA, 0xB5, 0x3D, 0x32, 0x60, 0x87, 0x23, 0xFD, 0x48, 0x7B, 0x31, 0x53, 0x82, 0xDF, 0x00, 0x3E,
|
||||||
|
0xBB, 0x57, 0x5C, 0x9E, 0xA0, 0x8C, 0x6F, 0xCA, 0x2E, 0x56, 0x87, 0x1A, 0xDB, 0x69, 0x17, 0xDF,
|
||||||
|
0xF6, 0xA8, 0x42, 0xD5, 0xC3, 0xFF, 0x7E, 0x28, 0xC6, 0x32, 0x67, 0xAC, 0x73, 0x55, 0x4F, 0x8C,
|
||||||
|
0xB0, 0x27, 0x5B, 0x69, 0xC8, 0x58, 0xCA, 0xBB, 0x5D, 0xA3, 0xFF, 0xE1, 0xA0, 0x11, 0xF0, 0xB8,
|
||||||
|
0x98, 0x3D, 0xFA, 0x10, 0xB8, 0x83, 0x21, 0xFD, 0x6C, 0xB5, 0xFC, 0x4A, 0x5B, 0xD3, 0xD1, 0x2D,
|
||||||
|
0x79, 0xE4, 0x53, 0x9A, 0x65, 0x45, 0xF8, 0xB6, 0xBC, 0x49, 0x8E, 0xD2, 0x90, 0x97, 0xFB, 0x4B,
|
||||||
|
0xDA, 0xF2, 0xDD, 0xE1, 0x33, 0x7E, 0xCB, 0xA4, 0x41, 0x13, 0xFB, 0x62, 0xE8, 0xC6, 0xE4, 0xCE,
|
||||||
|
0xDA, 0xCA, 0x20, 0xEF, 0x01, 0x4C, 0x77, 0x36, 0xFE, 0x9E, 0x7E, 0xD0, 0xB4, 0x1F, 0xF1, 0x2B,
|
||||||
|
0x4D, 0xDA, 0xDB, 0x95, 0x98, 0x91, 0x90, 0xAE, 0x71, 0x8E, 0xAD, 0xEA, 0xA0, 0xD5, 0x93, 0x6B,
|
||||||
|
0xD0, 0xD1, 0x8E, 0xD0, 0xE0, 0x25, 0xC7, 0xAF, 0x2F, 0x5B, 0x3C, 0x8E, 0xB7, 0x94, 0x75, 0x8E,
|
||||||
|
0xFB, 0xE2, 0xF6, 0x8F, 0x64, 0x2B, 0x12, 0xF2, 0x12, 0xB8, 0x88, 0x88, 0x1C, 0xF0, 0x0D, 0x90,
|
||||||
|
0xA0, 0x5E, 0xAD, 0x4F, 0x1C, 0xC3, 0x8F, 0x68, 0x91, 0xF1, 0xCF, 0xD1, 0xAD, 0xC1, 0xA8, 0xB3,
|
||||||
|
0x18, 0x22, 0x2F, 0x2F, 0x77, 0x17, 0x0E, 0xBE, 0xFE, 0x2D, 0x75, 0xEA, 0xA1, 0x1F, 0x02, 0x8B,
|
||||||
|
0x0F, 0xCC, 0xA0, 0xE5, 0xE8, 0x74, 0x6F, 0xB5, 0xD6, 0xF3, 0xAC, 0x18, 0x99, 0xE2, 0x89, 0xCE,
|
||||||
|
0xE0, 0x4F, 0xA8, 0xB4, 0xB7, 0xE0, 0x13, 0xFD, 0x81, 0x3B, 0xC4, 0x7C, 0xD9, 0xA8, 0xAD, 0xD2,
|
||||||
|
0x66, 0xA2, 0x5F, 0x16, 0x05, 0x77, 0x95, 0x80, 0x14, 0x73, 0xCC, 0x93, 0x77, 0x14, 0x1A, 0x21,
|
||||||
|
0x65, 0x20, 0xAD, 0xE6, 0x86, 0xFA, 0xB5, 0x77, 0xF5, 0x42, 0x54, 0xC7, 0xCF, 0x35, 0x9D, 0xFB,
|
||||||
|
0x0C, 0xAF, 0xCD, 0xEB, 0xA0, 0x89, 0x3E, 0x7B, 0xD3, 0x1B, 0x41, 0xD6, 0x49, 0x7E, 0x1E, 0xAE,
|
||||||
|
0x2D, 0x0E, 0x25, 0x00, 0x5E, 0xB3, 0x71, 0x20, 0xBB, 0x00, 0x68, 0x22, 0xAF, 0xE0, 0xB8, 0x57,
|
||||||
|
0x9B, 0x36, 0x64, 0x24, 0x1E, 0xB9, 0x09, 0xF0, 0x1D, 0x91, 0x63, 0x55, 0xAA, 0xA6, 0xDF, 0x59,
|
||||||
|
0x89, 0x43, 0xC1, 0x78, 0x7F, 0x53, 0x5A, 0xD9, 0xA2, 0x5B, 0x7D, 0x20, 0xC5, 0xB9, 0xE5, 0x02,
|
||||||
|
0x76, 0x03, 0x26, 0x83, 0xA9, 0xCF, 0x95, 0x62, 0x68, 0x19, 0xC8, 0x11, 0x41, 0x4A, 0x73, 0x4E,
|
||||||
|
0xCA, 0x2D, 0x47, 0xB3, 0x4A, 0xA9, 0x14, 0x7B, 0x52, 0x00, 0x51, 0x1B, 0x15, 0x29, 0x53, 0x9A,
|
||||||
|
0x3F, 0x57, 0x0F, 0xD6, 0xE4, 0xC6, 0x9B, 0xBC, 0x76, 0xA4, 0x60, 0x2B, 0x00, 0x74, 0xE6, 0x81,
|
||||||
|
0xB5, 0x6F, 0xBA, 0x08, 0x1F, 0xE9, 0x1B, 0x57, 0x6B, 0xEC, 0x96, 0xF2, 0x15, 0xD9, 0x0D, 0x2A,
|
||||||
|
0x21, 0x65, 0x63, 0xB6, 0xB6, 0xF9, 0xB9, 0xE7, 0x2E, 0x05, 0x34, 0xFF, 0x64, 0x56, 0x85, 0xC5,
|
||||||
|
0x5D, 0x2D, 0xB0, 0x53, 0xA1, 0x8F, 0x9F, 0xA9, 0x99, 0x47, 0xBA, 0x08, 0x6A, 0x07, 0x85, 0x6E,
|
||||||
|
0xE9, 0x70, 0x7A, 0x4B, 0x44, 0x29, 0xB3, 0xB5, 0x2E, 0x09, 0x75, 0xDB, 0x23, 0x26, 0x19, 0xC4,
|
||||||
|
0xB0, 0xA6, 0x6E, 0xAD, 0x7D, 0xDF, 0xA7, 0x49, 0xB8, 0x60, 0xEE, 0x9C, 0x66, 0xB2, 0xED, 0x8F,
|
||||||
|
0x71, 0x8C, 0xAA, 0xEC, 0xFF, 0x17, 0x9A, 0x69, 0x6C, 0x52, 0x64, 0x56, 0xE1, 0x9E, 0xB1, 0xC2,
|
||||||
|
0xA5, 0x02, 0x36, 0x19, 0x29, 0x4C, 0x09, 0x75, 0x40, 0x13, 0x59, 0xA0, 0x3E, 0x3A, 0x18, 0xE4,
|
||||||
|
0x9A, 0x98, 0x54, 0x3F, 0x65, 0x9D, 0x42, 0x5B, 0xD6, 0xE4, 0x8F, 0x6B, 0xD6, 0x3F, 0xF7, 0x99,
|
||||||
|
0x07, 0x9C, 0xD2, 0xA1, 0xF5, 0x30, 0xE8, 0xEF, 0xE6, 0x38, 0x2D, 0x4D, 0xC1, 0x5D, 0x25, 0xF0,
|
||||||
|
0x86, 0x20, 0xDD, 0x4C, 0x26, 0xEB, 0x70, 0x84, 0xC6, 0xE9, 0x82, 0x63, 0x5E, 0xCC, 0x1E, 0x02,
|
||||||
|
0x3F, 0x6B, 0x68, 0x09, 0xC9, 0xEF, 0xBA, 0x3E, 0x14, 0x18, 0x97, 0x3C, 0xA1, 0x70, 0x6A, 0x6B,
|
||||||
|
0x84, 0x35, 0x7F, 0x68, 0x86, 0xE2, 0xA0, 0x52, 0x05, 0x53, 0x9C, 0xB7, 0x37, 0x07, 0x50, 0xAA,
|
||||||
|
0x1C, 0x84, 0x07, 0x3E, 0x5C, 0xAE, 0xDE, 0x7F, 0xEC, 0x44, 0x7D, 0x8E, 0xB8, 0xF2, 0x16, 0x57,
|
||||||
|
0x37, 0xDA, 0x3A, 0xB0, 0x0D, 0x0C, 0x50, 0xF0, 0x04, 0x1F, 0x1C, 0xF0, 0xFF, 0xB3, 0x00, 0x02,
|
||||||
|
0x1A, 0xF5, 0x0C, 0xAE, 0xB2, 0x74, 0xB5, 0x3C, 0x58, 0x7A, 0x83, 0x25, 0xBD, 0x21, 0x09, 0xDC,
|
||||||
|
0xF9, 0x13, 0x91, 0xD1, 0xF6, 0x2F, 0xA9, 0x7C, 0x73, 0x47, 0x32, 0x94, 0x01, 0x47, 0xF5, 0x22,
|
||||||
|
0x81, 0xE5, 0xE5, 0x3A, 0xDC, 0xDA, 0xC2, 0x37, 0x34, 0x76, 0xB5, 0xC8, 0xA7, 0xDD, 0xF3, 0x9A,
|
||||||
|
0x46, 0x61, 0x44, 0xA9, 0x0E, 0x03, 0xD0, 0x0F, 0x3E, 0xC7, 0xC8, 0xEC, 0x41, 0x1E, 0x75, 0xA4,
|
||||||
|
0x99, 0xCD, 0x38, 0xE2, 0x2F, 0x0E, 0xEA, 0x3B, 0xA1, 0xBB, 0x80, 0x32, 0x31, 0xB3, 0x3E, 0x18,
|
||||||
|
0x38, 0x8B, 0x54, 0x4E, 0x08, 0xB9, 0x6D, 0x4F, 0x03, 0x0D, 0x42, 0x6F, 0xBF, 0x04, 0x0A, 0xF6,
|
||||||
|
0x90, 0x12, 0xB8, 0x2C, 0x79, 0x7C, 0x97, 0x24, 0x72, 0xB0, 0x79, 0x56, 0xAF, 0x89, 0xAF, 0xBC,
|
||||||
|
0x1F, 0x77, 0x9A, 0xDE, 0x10, 0x08, 0x93, 0xD9, 0x12, 0xAE, 0x8B, 0xB3, 0x2E, 0x3F, 0xCF, 0xDC,
|
||||||
|
0x1F, 0x72, 0x12, 0x55, 0x24, 0x71, 0x6B, 0x2E, 0xE6, 0xDD, 0x1A, 0x50, 0x87, 0xCD, 0x84, 0x9F,
|
||||||
|
0x18, 0x47, 0x58, 0x7A, 0x17, 0xDA, 0x08, 0x74, 0xBC, 0x9A, 0x9F, 0xBC, 0x8C, 0x7D, 0x4B, 0xE9,
|
||||||
|
0x3A, 0xEC, 0x7A, 0xEC, 0xFA, 0x1D, 0x85, 0xDB, 0x66, 0x43, 0x09, 0x63, 0xD2, 0xC3, 0x64, 0xC4,
|
||||||
|
0x47, 0x18, 0x1C, 0xEF, 0x08, 0xD9, 0x15, 0x32, 0x37, 0x3B, 0x43, 0xDD, 0x16, 0xBA, 0xC2, 0x24,
|
||||||
|
0x43, 0x4D, 0xA1, 0x12, 0x51, 0xC4, 0x65, 0x2A, 0x02, 0x00, 0x94, 0x50, 0xDD, 0xE4, 0x3A, 0x13,
|
||||||
|
0x9E, 0xF8, 0xDF, 0x71, 0x55, 0x4E, 0x31, 0x10, 0xD6, 0x77, 0xAC, 0x81, 0x9B, 0x19, 0x11, 0x5F,
|
||||||
|
0xF1, 0x56, 0x35, 0x04, 0x6B, 0xC7, 0xA3, 0xD7, 0x3B, 0x18, 0x11, 0x3C, 0x09, 0xA5, 0x24, 0x59,
|
||||||
|
0xED, 0xE6, 0x8F, 0xF2, 0xFA, 0xFB, 0xF1, 0x97, 0x2C, 0xBF, 0xBA, 0x9E, 0x6E, 0x3C, 0x15, 0x1E,
|
||||||
|
0x70, 0x45, 0xE3, 0x86, 0xB1, 0x6F, 0xE9, 0xEA, 0x0A, 0x5E, 0x0E, 0x86, 0xB3, 0x2A, 0x3E, 0x5A,
|
||||||
|
0x1C, 0xE7, 0x1F, 0x77, 0xFA, 0x06, 0x3D, 0x4E, 0xB9, 0xDC, 0x65, 0x29, 0x0F, 0x1D, 0xE7, 0x99,
|
||||||
|
0xD6, 0x89, 0x3E, 0x80, 0x25, 0xC8, 0x66, 0x52, 0x78, 0xC9, 0x4C, 0x2E, 0x6A, 0xB3, 0x10, 0x9C,
|
||||||
|
0xBA, 0x0E, 0x15, 0xC6, 0x78, 0xEA, 0xE2, 0x94, 0x53, 0x3C, 0xFC, 0xA5, 0xF4, 0x2D, 0x0A, 0x1E,
|
||||||
|
0xA7, 0x4E, 0xF7, 0xF2, 0x3D, 0x2B, 0x1D, 0x36, 0x0F, 0x26, 0x39, 0x19, 0x60, 0x79, 0xC2, 0x19,
|
||||||
|
0x08, 0xA7, 0x23, 0x52, 0xB6, 0x12, 0x13, 0xF7, 0x6E, 0xFE, 0xAD, 0xEB, 0x66, 0x1F, 0xC3, 0xEA,
|
||||||
|
0x95, 0x45, 0xBC, 0xE3, 0x83, 0xC8, 0x7B, 0xA6, 0xD1, 0x37, 0x7F, 0xB1, 0x28, 0xFF, 0x8C, 0x01,
|
||||||
|
0xEF, 0xDD, 0x32, 0xC3, 0xA5, 0x5A, 0x6C, 0xBE, 0x85, 0x21, 0x58, 0x65, 0x02, 0x98, 0xAB, 0x68,
|
||||||
|
0x0F, 0xA5, 0xCE, 0xEE, 0x3B, 0x95, 0x2F, 0xDB, 0xAD, 0x7D, 0xEF, 0x2A, 0x84, 0x2F, 0x6E, 0x5B,
|
||||||
|
0x28, 0xB6, 0x21, 0x15, 0x70, 0x61, 0x07, 0x29, 0x75, 0x47, 0xDD, 0xEC, 0x10, 0x15, 0x9F, 0x61,
|
||||||
|
0x30, 0xA8, 0xCC, 0x13, 0x96, 0xBD, 0x61, 0xEB, 0x1E, 0xFE, 0x34, 0x03, 0xCF, 0x63, 0x03, 0xAA,
|
||||||
|
0x90, 0x5C, 0x73, 0xB5, 0x39, 0xA2, 0x70, 0x4C, 0x0B, 0x9E, 0x9E, 0xD5, 0x14, 0xDE, 0xAA, 0xCB,
|
||||||
|
0xBC, 0x86, 0xCC, 0xEE, 0xA7, 0x2C, 0x62, 0x60, 0xAB, 0x5C, 0xAB, 0x9C, 0x6E, 0x84, 0xF3, 0xB2,
|
||||||
|
0xAF, 0x1E, 0x8B, 0x64, 0xCA, 0xF0, 0xBD, 0x19, 0xB9, 0x69, 0x23, 0xA0, 0x50, 0xBB, 0x5A, 0x65,
|
||||||
|
0x32, 0x5A, 0x68, 0x40, 0xB3, 0xB4, 0x2A, 0x3C, 0xD5, 0xE9, 0x9E, 0x31, 0xF7, 0xB8, 0x21, 0xC0,
|
||||||
|
0x19, 0x0B, 0x54, 0x9B, 0x99, 0xA0, 0x5F, 0x87, 0x7E, 0x99, 0xF7, 0x95, 0xA8, 0x7D, 0x3D, 0x62,
|
||||||
|
0x9A, 0x88, 0x37, 0xF8, 0x77, 0x2D, 0xE3, 0x97, 0x5F, 0x93, 0xED, 0x11, 0x81, 0x12, 0x68, 0x16,
|
||||||
|
0x29, 0x88, 0x35, 0x0E, 0xD6, 0x1F, 0xE6, 0xC7, 0xA1, 0xDF, 0xDE, 0x96, 0x99, 0xBA, 0x58, 0x78,
|
||||||
|
0xA5, 0x84, 0xF5, 0x57, 0x63, 0x72, 0x22, 0x1B, 0xFF, 0xC3, 0x83, 0x9B, 0x96, 0x46, 0xC2, 0x1A,
|
||||||
|
0xEB, 0x0A, 0xB3, 0xCD, 0x54, 0x30, 0x2E, 0x53, 0xE4, 0x48, 0xD9, 0x8F, 0x28, 0x31, 0xBC, 0x6D,
|
||||||
|
0xEF, 0xF2, 0xEB, 0x58, 0xEA, 0xFF, 0xC6, 0x34, 0x61, 0xED, 0x28, 0xFE, 0x73, 0x3C, 0x7C, 0xEE,
|
||||||
|
0xD9, 0x14, 0x4A, 0x5D, 0xE3, 0xB7, 0x64, 0xE8, 0x14, 0x5D, 0x10, 0x42, 0xE0, 0x13, 0x3E, 0x20,
|
||||||
|
0xB6, 0xE2, 0xEE, 0x45, 0xEA, 0xAB, 0xAA, 0xA3, 0x15, 0x4F, 0x6C, 0xDB, 0xD0, 0x4F, 0xCB, 0xFA,
|
||||||
|
0x42, 0xF4, 0x42, 0xC7, 0xB5, 0xBB, 0x6A, 0xEF, 0x1D, 0x3B, 0x4F, 0x65, 0x05, 0x21, 0xCD, 0x41,
|
||||||
|
0x9E, 0x79, 0x1E, 0xD8, 0xC7, 0x4D, 0x85, 0x86, 0x6A, 0x47, 0x4B, 0xE4, 0x50, 0x62, 0x81, 0x3D,
|
||||||
|
0xF2, 0xA1, 0x62, 0xCF, 0x46, 0x26, 0x8D, 0x5B, 0xA0, 0x83, 0x88, 0xFC, 0xA3, 0xB6, 0xC7, 0xC1,
|
||||||
|
0xC3, 0x24, 0x15, 0x7F, 0x92, 0x74, 0xCB, 0x69, 0x0B, 0x8A, 0x84, 0x47, 0x85, 0xB2, 0x92, 0x56,
|
||||||
|
0x00, 0xBF, 0x5B, 0x09, 0x9D, 0x48, 0x19, 0xAD, 0x74, 0xB1, 0x62, 0x14, 0x00, 0x0E, 0x82, 0x23,
|
||||||
|
0x2A, 0x8D, 0x42, 0x58, 0xEA, 0xF5, 0x55, 0x0C, 0x3E, 0xF4, 0xAD, 0x1D, 0x61, 0x70, 0x3F, 0x23,
|
||||||
|
0x92, 0xF0, 0x72, 0x33, 0x41, 0x7E, 0x93, 0x8D, 0xF1, 0xEC, 0x5F, 0xD6, 0xDB, 0x3B, 0x22, 0x6C,
|
||||||
|
0x59, 0x37, 0xDE, 0x7C, 0x60, 0x74, 0xEE, 0xCB, 0xA7, 0xF2, 0x85, 0x40, 0x6E, 0x32, 0x77, 0xCE,
|
||||||
|
0x84, 0x80, 0x07, 0xA6, 0x9E, 0x50, 0xF8, 0x19, 0x55, 0xD8, 0xEF, 0xE8, 0x35, 0x97, 0xD9, 0x61,
|
||||||
|
0xAA, 0xA7, 0x69, 0xA9, 0xC2, 0x06, 0x0C, 0xC5, 0xFC, 0xAB, 0x04, 0x5A, 0xDC, 0xCA, 0x0B, 0x80,
|
||||||
|
0x2E, 0x7A, 0x44, 0x9E, 0x84, 0x34, 0x45, 0xC3, 0x05, 0x67, 0xD5, 0xFD, 0xC9, 0x9E, 0x1E, 0x0E,
|
||||||
|
0xD3, 0xDB, 0x73, 0xDB, 0xCD, 0x88, 0x55, 0x10, 0x79, 0xDA, 0x5F, 0x67, 0x40, 0x43, 0x67, 0xE3,
|
||||||
|
0x65, 0x34, 0xC4, 0xC5, 0xD8, 0x38, 0x3E, 0x71, 0x9E, 0xF8, 0x28, 0x3D, 0x20, 0xFF, 0x6D, 0xF1,
|
||||||
|
0xE7, 0x21, 0x3E, 0x15, 0x4A, 0x3D, 0xB0, 0x8F, 0x2B, 0x9F, 0xE3, 0xE6, 0xF7, 0xAD, 0x83, 0xDB,
|
||||||
|
0x68, 0x5A, 0x3D, 0xE9, 0xF7, 0x40, 0x81, 0x94, 0x1C, 0x26, 0x4C, 0xF6, 0x34, 0x29, 0x69, 0x94,
|
||||||
|
0xF7, 0x20, 0x15, 0x41, 0xF7, 0xD4, 0x02, 0x76, 0x2E, 0x6B, 0xF4, 0xBC, 0x68, 0x00, 0xA2, 0xD4,
|
||||||
|
0x71, 0x24, 0x08, 0xD4, 0x6A, 0xF4, 0x20, 0x33, 0xB7, 0xD4, 0xB7, 0x43, 0xAF, 0x61, 0x00, 0x50,
|
||||||
|
0x2E, 0xF6, 0x39, 0x1E, 0x46, 0x45, 0x24, 0x97, 0x74, 0x4F, 0x21, 0x14, 0x40, 0x88, 0x8B, 0xBF,
|
||||||
|
0x1D, 0xFC, 0x95, 0x4D, 0xAF, 0x91, 0xB5, 0x96, 0xD3, 0xDD, 0xF4, 0x70, 0x45, 0x2F, 0xA0, 0x66,
|
||||||
|
0xEC, 0x09, 0xBC, 0xBF, 0x85, 0x97, 0xBD, 0x03, 0xD0, 0x6D, 0xAC, 0x7F, 0x04, 0x85, 0xCB, 0x31,
|
||||||
|
0xB3, 0x27, 0xEB, 0x96, 0x41, 0x39, 0xFD, 0x55, 0xE6, 0x47, 0x25, 0xDA, 0x9A, 0x0A, 0xCA, 0xAB,
|
||||||
|
0x25, 0x78, 0x50, 0x28, 0xF4, 0x29, 0x04, 0x53, 0xDA, 0x86, 0x2C, 0x0A, 0xFB, 0x6D, 0xB6, 0xE9,
|
||||||
|
0x62, 0x14, 0xDC, 0x68, 0x00, 0x69, 0x48, 0xD7, 0xA4, 0xC0, 0x0E, 0x68, 0xEE, 0x8D, 0xA1, 0x27,
|
||||||
|
0xA2, 0xFE, 0x3F, 0x4F, 0x8C, 0xAD, 0x87, 0xE8, 0x06, 0xE0, 0x8C, 0xB5, 0xB6, 0xD6, 0xF4, 0x7A,
|
||||||
|
0x7C, 0x1E, 0xCE, 0xAA, 0xEC, 0x5F, 0x37, 0xD3, 0x99, 0xA3, 0x78, 0xCE, 0x42, 0x2A, 0x6B, 0x40,
|
||||||
|
0x35, 0x9E, 0xFE, 0x20, 0xB9, 0x85, 0xF3, 0xD9, 0xAB, 0xD7, 0x39, 0xEE, 0x8B, 0x4E, 0x12, 0x3B,
|
||||||
|
0xF7, 0xFA, 0xC9, 0x1D, 0x56, 0x18, 0x6D, 0x4B, 0x31, 0x66, 0xA3, 0x26, 0xB2, 0x97, 0xE3, 0xEA,
|
||||||
|
0x74, 0xFA, 0x6E, 0x3A, 0x32, 0x43, 0x5B, 0xDD, 0xF7, 0xE7, 0x41, 0x68, 0xFB, 0x20, 0x78, 0xCA,
|
||||||
|
0x4E, 0xF5, 0x0A, 0xFB, 0x97, 0xB3, 0xFE, 0xD8, 0xAC, 0x56, 0x40, 0x45, 0x27, 0x95, 0x48, 0xBA,
|
||||||
|
0x3A, 0x3A, 0x53, 0x55, 0x87, 0x8D, 0x83, 0x20, 0xB7, 0xA9, 0x6B, 0xFE, 0x4B, 0x95, 0x96, 0xD0,
|
||||||
|
0xBC, 0x67, 0xA8, 0x55, 0x58, 0x9A, 0x15, 0xA1, 0x63, 0x29, 0xA9, 0xCC, 0x33, 0xDB, 0xE1, 0x99,
|
||||||
|
0x56, 0x4A, 0x2A, 0xA6, 0xF9, 0x25, 0x31, 0x3F, 0x1C, 0x7E, 0xF4, 0x5E, 0x7C, 0x31, 0x29, 0x90,
|
||||||
|
0x02, 0xE8, 0xF8, 0xFD, 0x70, 0x2F, 0x27, 0x04, 0x5C, 0x15, 0xBB, 0x80, 0xE3, 0x2C, 0x28, 0x05,
|
||||||
|
0x48, 0x15, 0xC1, 0x95, 0x22, 0x6D, 0xC6, 0xE4, 0x3F, 0x13, 0xC1, 0x48, 0xDC, 0x86, 0x0F, 0xC7,
|
||||||
|
0xEE, 0xC9, 0xF9, 0x07, 0x0F, 0x1F, 0x04, 0x41, 0xA4, 0x79, 0x47, 0x40, 0x17, 0x6E, 0x88, 0x5D,
|
||||||
|
0xEB, 0x51, 0x5F, 0x32, 0xD1, 0xC0, 0x9B, 0xD5, 0x8F, 0xC1, 0xBC, 0xF2, 0x64, 0x35, 0x11, 0x41,
|
||||||
|
0x34, 0x78, 0x7B, 0x25, 0x60, 0x9C, 0x2A, 0x60, 0xA3, 0xE8, 0xF8, 0xDF, 0x1B, 0x6C, 0x63, 0x1F,
|
||||||
|
0xC2, 0xB4, 0x12, 0x0E, 0x9E, 0x32, 0xE1, 0x02, 0xD1, 0x4F, 0x66, 0xAF, 0x15, 0x81, 0xD1, 0xCA,
|
||||||
|
0xE0, 0x95, 0x23, 0x6B, 0xE1, 0x92, 0x3E, 0x33, 0x62, 0x0B, 0x24, 0x3B, 0x22, 0xB9, 0xBE, 0xEE,
|
||||||
|
0x0E, 0xA2, 0xB2, 0x85, 0x99, 0x0D, 0xBA, 0xE6, 0x8C, 0x0C, 0x72, 0xDE, 0x28, 0xF7, 0xA2, 0x2D,
|
||||||
|
0x45, 0x78, 0x12, 0xD0, 0xFD, 0x94, 0xB7, 0x95, 0x62, 0x08, 0x7D, 0x64, 0xF0, 0xF5, 0xCC, 0xE7,
|
||||||
|
0x6F, 0xA3, 0x49, 0x54, 0xFA, 0x48, 0x7D, 0x87, 0x27, 0xFD, 0x9D, 0xC3, 0x1E, 0x8D, 0x3E, 0xF3,
|
||||||
|
0x41, 0x63, 0x47, 0x0A, 0x74, 0xFF, 0x2E, 0x99, 0xAB, 0x6E, 0x6F, 0x3A, 0x37, 0xFD, 0xF8, 0xF4,
|
||||||
|
0x60, 0xDC, 0x12, 0xA8, 0xF8, 0xDD, 0xEB, 0xA1, 0x4C, 0xE1, 0x1B, 0x99, 0x0D, 0x6B, 0x6E, 0xDB,
|
||||||
|
0x10, 0x55, 0x7B, 0xC6, 0x37, 0x2C, 0x67, 0x6D, 0x3B, 0xD4, 0x65, 0x27, 0x04, 0xE8, 0xD0, 0xDC,
|
||||||
|
0xC7, 0x0D, 0x29, 0xF1, 0xA3, 0xFF, 0x00, 0xCC, 0x92, 0x0F, 0x39, 0xB5, 0x0B, 0xED, 0x0F, 0x69,
|
||||||
|
0xFB, 0x9F, 0x7B, 0x66, 0x9C, 0x7D, 0xDB, 0xCE, 0x0B, 0xCF, 0x91, 0xA0, 0xA3, 0x5E, 0x15, 0xD9,
|
||||||
|
0x88, 0x2F, 0x13, 0xBB, 0x24, 0xAD, 0x5B, 0x51, 0xBF, 0x79, 0x94, 0x7B, 0xEB, 0xD6, 0x3B, 0x76,
|
||||||
|
0xB3, 0x2E, 0x39, 0x37, 0x79, 0x59, 0x11, 0xCC, 0x97, 0xE2, 0x26, 0x80, 0x2D, 0x31, 0x2E, 0xF4,
|
||||||
|
0xA7, 0xAD, 0x42, 0x68, 0x3B, 0x2B, 0x6A, 0xC6, 0xCC, 0x4C, 0x75, 0x12, 0x1C, 0xF1, 0x2E, 0x78,
|
||||||
|
0x37, 0x42, 0x12, 0x6A, 0xE7, 0x51, 0x92, 0xB7, 0xE6, 0xBB, 0xA1, 0x06, 0x50, 0x63, 0xFB, 0x4B,
|
||||||
|
0x18, 0x10, 0x6B, 0x1A, 0xFA, 0xED, 0xCA, 0x11, 0xD8, 0xBD, 0x25, 0x3D, 0xC9, 0xC3, 0xE1, 0xE2,
|
||||||
|
0x59, 0x16, 0x42, 0x44, 0x86, 0x13, 0x12, 0x0A, 0x6E, 0xEC, 0x0C, 0xD9, 0x2A, 0xEA, 0xAB, 0xD5,
|
||||||
|
0x4E, 0x67, 0xAF, 0x64, 0x5F, 0xA8, 0x86, 0xDA, 0x88, 0xE9, 0xBF, 0xBE, 0xFE, 0xC3, 0xE4, 0x64,
|
||||||
|
0x57, 0x80, 0xBC, 0x9D, 0x86, 0xC0, 0xF7, 0xF0, 0xF8, 0x7B, 0x78, 0x60, 0x4D, 0x60, 0x03, 0x60,
|
||||||
|
0x46, 0x83, 0xFD, 0xD1, 0xB0, 0x1F, 0x38, 0xF6, 0x04, 0xAE, 0x45, 0x77, 0xCC, 0xFC, 0x36, 0xD7,
|
||||||
|
0x33, 0x6B, 0x42, 0x83, 0x71, 0xAB, 0x1E, 0xF0, 0x87, 0x41, 0x80, 0xB0, 0x5F, 0x5E, 0x00, 0x3C,
|
||||||
|
0xBE, 0x57, 0xA0, 0x77, 0x24, 0xAE, 0xE8, 0xBD, 0x99, 0x42, 0x46, 0x55, 0x61, 0x2E, 0x58, 0xBF,
|
||||||
|
0x8F, 0xF4, 0x58, 0x4E, 0xA2, 0xFD, 0xDD, 0xF2, 0x38, 0xEF, 0x74, 0xF4, 0xC2, 0xBD, 0x89, 0x87,
|
||||||
|
0xC3, 0xF9, 0x66, 0x53, 0x74, 0x8E, 0xB3, 0xC8, 0x55, 0xF2, 0x75, 0xB4, 0xB9, 0xD9, 0xFC, 0x46,
|
||||||
|
0x61, 0x26, 0xEB, 0x7A, 0x84, 0xDF, 0x1D, 0x8B, 0x79, 0x0E, 0x6A, 0x84, 0xE2, 0x95, 0x5F, 0x91,
|
||||||
|
0x8E, 0x59, 0x6E, 0x46, 0x70, 0x57, 0xB4, 0x20, 0x91, 0x55, 0xD5, 0x8C, 0x4C, 0xDE, 0x02, 0xC9,
|
||||||
|
0xE1, 0xAC, 0x0B, 0xB9, 0xD0, 0x05, 0x82, 0xBB, 0x48, 0x62, 0xA8, 0x11, 0x9E, 0xA9, 0x74, 0x75,
|
||||||
|
0xB6, 0x19, 0x7F, 0xB7, 0x09, 0xDC, 0xA9, 0xE0, 0xA1, 0x09, 0x2D, 0x66, 0x33, 0x46, 0x32, 0xC4,
|
||||||
|
0x02, 0x1F, 0x5A, 0xE8, 0x8C, 0xBE, 0xF0, 0x09, 0x25, 0xA0, 0x99, 0x4A, 0x10, 0xFE, 0x6E, 0x1D,
|
||||||
|
0x1D, 0x3D, 0xB9, 0x1A, 0xDF, 0xA4, 0xA5, 0x0B, 0x0F, 0xF2, 0x86, 0xA1, 0x69, 0xF1, 0x68, 0x28,
|
||||||
|
0x83, 0xDA, 0xB7, 0xDC, 0xFE, 0x06, 0x39, 0x57, 0x9B, 0xCE, 0xE2, 0xA1, 0x52, 0x7F, 0xCD, 0x4F,
|
||||||
|
0x01, 0x5E, 0x11, 0x50, 0xFA, 0x83, 0x06, 0xA7, 0xC4, 0xB5, 0x02, 0xA0, 0x27, 0xD0, 0xE6, 0x0D,
|
||||||
|
0x27, 0x8C, 0xF8, 0x9A, 0x41, 0x86, 0x3F, 0x77, 0x06, 0x4C, 0x60, 0xC3, 0xB5, 0x06, 0xA8, 0x61,
|
||||||
|
0x28, 0x7A, 0x17, 0xF0, 0xE0, 0x86, 0xF5, 0xC0, 0xAA, 0x58, 0x60, 0x00, 0x62, 0x7D, 0xDC, 0x30,
|
||||||
|
0xD7, 0x9E, 0xE6, 0x11, 0x63, 0xEA, 0x38, 0x23, 0x94, 0xDD, 0xC2, 0x53, 0x34, 0x16, 0xC2, 0xC2,
|
||||||
|
0x56, 0xEE, 0xCB, 0xBB, 0xDE, 0xB6, 0xBC, 0x90, 0xA1, 0x7D, 0xFC, 0xEB, 0x76, 0x1D, 0x59, 0xCE,
|
||||||
|
0x09, 0xE4, 0x05, 0x6F, 0x88, 0x01, 0x7C, 0x4B, 0x3D, 0x0A, 0x72, 0x39, 0x24, 0x7C, 0x92, 0x7C,
|
||||||
|
0x5F, 0x72, 0xE3, 0x86, 0xB9, 0x9D, 0x4D, 0x72, 0xB4, 0x5B, 0xC1, 0x1A, 0xFC, 0xB8, 0x9E, 0xD3,
|
||||||
|
0x78, 0x55, 0x54, 0xED, 0xB5, 0xA5, 0xFC, 0x08, 0xD3, 0x7C, 0x3D, 0xD8, 0xC4, 0x0F, 0xAD, 0x4D,
|
||||||
|
0x5E, 0xEF, 0x50, 0x1E, 0xF8, 0xE6, 0x61, 0xB1, 0xD9, 0x14, 0x85, 0xA2, 0x3C, 0x13, 0x51, 0x6C,
|
||||||
|
0xE7, 0xC7, 0xD5, 0x6F, 0xC4, 0x4E, 0xE1, 0x56, 0xCE, 0xBF, 0x2A, 0x36, 0x37, 0xC8, 0xC6, 0xDD,
|
||||||
|
0x34, 0x32, 0x9A, 0xD7, 0x12, 0x82, 0x63, 0x92, 0x8E, 0xFA, 0x0E, 0x67, 0xE0, 0x00, 0x60, 0x40,
|
||||||
|
0x37, 0xCE, 0x39, 0x3A, 0xCF, 0xF5, 0xFA, 0xD3, 0x37, 0x77, 0xC2, 0xAB, 0x1B, 0x2D, 0xC5, 0x5A,
|
||||||
|
0x9E, 0x67, 0xB0, 0x5C, 0x42, 0x37, 0xA3, 0x4F, 0x40, 0x27, 0x82, 0xD3, 0xBE, 0x9B, 0xBC, 0x99,
|
||||||
|
0x9D, 0x8E, 0x11, 0xD5, 0x15, 0x73, 0x0F, 0xBF, 0x7E, 0x1C, 0x2D, 0xD6, 0x7B, 0xC4, 0x00, 0xC7,
|
||||||
|
0x6B, 0x1B, 0x8C, 0xB7, 0x45, 0x90, 0xA1, 0x21, 0xBE, 0xB1, 0x6E, 0xB2, 0xB4, 0x6E, 0x36, 0x6A,
|
||||||
|
0x2F, 0xAB, 0x48, 0x57, 0x79, 0x6E, 0x94, 0xBC, 0xD2, 0x76, 0xA3, 0xC6, 0xC8, 0xC2, 0x49, 0x65,
|
||||||
|
0xEE, 0xF8, 0x0F, 0x53, 0x7D, 0xDE, 0x8D, 0x46, 0x1D, 0x0A, 0x73, 0xD5, 0xC6, 0x4D, 0xD0, 0x4C,
|
||||||
|
0xDB, 0xBB, 0x39, 0x29, 0x50, 0x46, 0xBA, 0xA9, 0xE8, 0x26, 0x95, 0xAC, 0x04, 0xE3, 0x5E, 0xBE,
|
||||||
|
0xF0, 0xD5, 0xFA, 0xA1, 0x9A, 0x51, 0x2D, 0x6A, 0xE2, 0x8C, 0xEF, 0x63, 0x22, 0xEE, 0x86, 0x9A,
|
||||||
|
0xB8, 0xC2, 0x89, 0xC0, 0xF6, 0x2E, 0x24, 0x43, 0xAA, 0x03, 0x1E, 0xA5, 0xA4, 0xD0, 0xF2, 0x9C,
|
||||||
|
0xBA, 0x61, 0xC0, 0x83, 0x4D, 0x6A, 0xE9, 0x9B, 0x50, 0x15, 0xE5, 0x8F, 0xD6, 0x5B, 0x64, 0xBA,
|
||||||
|
0xF9, 0xA2, 0x26, 0x28, 0xE1, 0x3A, 0x3A, 0xA7, 0x86, 0x95, 0xA9, 0x4B, 0xE9, 0x62, 0x55, 0xEF,
|
||||||
|
0xD3, 0xEF, 0x2F, 0xC7, 0xDA, 0xF7, 0x52, 0xF7, 0x69, 0x6F, 0x04, 0x3F, 0x59, 0x0A, 0xFA, 0x77,
|
||||||
|
0x15, 0xA9, 0xE4, 0x80, 0x01, 0x86, 0xB0, 0x87, 0xAD, 0xE6, 0x09, 0x9B, 0x93, 0xE5, 0x3E, 0x3B,
|
||||||
|
0x5A, 0xFD, 0x90, 0xE9, 0x97, 0xD7, 0x34, 0x9E, 0xD9, 0xB7, 0xF0, 0x2C, 0x51, 0x8B, 0x2B, 0x02,
|
||||||
|
0x3A, 0xAC, 0xD5, 0x96, 0x7D, 0xA6, 0x7D, 0x01, 0xD6, 0x3E, 0xCF, 0xD1, 0x28, 0x2D, 0x7D, 0x7C,
|
||||||
|
0xCF, 0x25, 0x9F, 0x1F, 0x9B, 0xB8, 0xF2, 0xAD, 0x72, 0xB4, 0xD6, 0x5A, 0x4C, 0xF5, 0x88, 0x5A,
|
||||||
|
0x71, 0xAC, 0x29, 0xE0, 0xE6, 0xA5, 0x19, 0xE0, 0xFD, 0xAC, 0xB0, 0x47, 0x9B, 0xFA, 0x93, 0xED,
|
||||||
|
0x8D, 0xC4, 0xD3, 0xE8, 0xCC, 0x57, 0x3B, 0x28, 0x29, 0x66, 0xD5, 0xF8, 0x28, 0x2E, 0x13, 0x79,
|
||||||
|
0x91, 0x01, 0x5F, 0x78, 0x55, 0x60, 0x75, 0xED, 0x44, 0x0E, 0x96, 0xF7, 0x8C, 0x5E, 0xD3, 0xE3,
|
||||||
|
0xD4, 0x6D, 0x05, 0x15, 0xBA, 0x6D, 0xF4, 0x88, 0x25, 0x61, 0xA1, 0x03, 0xBD, 0xF0, 0x64, 0x05,
|
||||||
|
0x15, 0x9E, 0xEB, 0xC3, 0xA2, 0x57, 0x90, 0x3C, 0xEC, 0x1A, 0x27, 0x97, 0x2A, 0x07, 0x3A, 0xA9,
|
||||||
|
0x9B, 0x6D, 0x3F, 0x1B, 0xF5, 0x21, 0x63, 0x1E, 0xFB, 0x66, 0x9C, 0xF5, 0x19, 0xF3, 0xDC, 0x26,
|
||||||
|
0x28, 0xD9, 0x33, 0x75, 0xF5, 0xFD, 0x55, 0xB1, 0x82, 0x34, 0x56, 0x03, 0xBB, 0x3C, 0xBA, 0x8A,
|
||||||
|
0x11, 0x77, 0x51, 0x28, 0xF8, 0xD9, 0x0A, 0xC2, 0x67, 0x51, 0xCC, 0xAB, 0x5F, 0x92, 0xAD, 0xCC,
|
||||||
|
0x51, 0x17, 0xE8, 0x4D, 0x8E, 0xDC, 0x30, 0x38, 0x62, 0x58, 0x9D, 0x37, 0x91, 0xF9, 0x20, 0x93,
|
||||||
|
0xC2, 0x90, 0x7A, 0xEA, 0xCE, 0x7B, 0x3E, 0xFB, 0x64, 0xCE, 0x21, 0x51, 0x32, 0xBE, 0x4F, 0x77,
|
||||||
|
0x7E, 0xE3, 0xB6, 0xA8, 0x46, 0x3D, 0x29, 0xC3, 0x69, 0x53, 0xDE, 0x48, 0x80, 0xE6, 0x13, 0x64,
|
||||||
|
0x10, 0x08, 0xAE, 0xA2, 0x24, 0xB2, 0x6D, 0xDD, 0xFD, 0x2D, 0x85, 0x69, 0x66, 0x21, 0x07, 0x09,
|
||||||
|
0x0A, 0x46, 0x9A, 0xB3, 0xDD, 0xC0, 0x45, 0x64, 0xCF, 0xDE, 0x6C, 0x58, 0xAE, 0xC8, 0x20, 0x1C,
|
||||||
|
0xDD, 0xF7, 0xBE, 0x5B, 0x40, 0x8D, 0x58, 0x1B, 0x7F, 0x01, 0xD2, 0xCC, 0xBB, 0xE3, 0xB4, 0x6B,
|
||||||
|
0x7E, 0x6A, 0xA2, 0xDD, 0x45, 0xFF, 0x59, 0x3A, 0x44, 0x0A, 0x35, 0x3E, 0xD5, 0xCD, 0xB4, 0xBC,
|
||||||
|
0xA8, 0xCE, 0xEA, 0x72, 0xBB, 0x84, 0x64, 0xFA, 0xAE, 0x12, 0x66, 0x8D, 0x47, 0x6F, 0x3C, 0xBF,
|
||||||
|
0x63, 0xE4, 0x9B, 0xD2, 0x9E, 0x5D, 0x2F, 0x54, 0x1B, 0x77, 0xC2, 0xAE, 0x70, 0x63, 0x4E, 0xF6,
|
||||||
|
0x8D, 0x0D, 0x0E, 0x74, 0x57, 0x13, 0x5B, 0xE7, 0x71, 0x16, 0x72, 0xF8, 0x5D, 0x7D, 0x53, 0xAF,
|
||||||
|
0x08, 0xCB, 0x40, 0x40, 0xCC, 0xE2, 0xB4, 0x4E, 0x6A, 0x46, 0xD2, 0x34, 0x84, 0xAF, 0x15, 0x01,
|
||||||
|
0x28, 0x04, 0xB0, 0xE1, 0x1D, 0x3A, 0x98, 0x95, 0xB4, 0x9F, 0xB8, 0x06, 0x48, 0xA0, 0x6E, 0xCE,
|
||||||
|
0x82, 0x3B, 0x3F, 0x6F, 0x82, 0xAB, 0x20, 0x35, 0x4B, 0x1D, 0x1A, 0x01, 0xF8, 0x27, 0x72, 0x27,
|
||||||
|
0xB1, 0x60, 0x15, 0x61, 0xDC, 0x3F, 0x93, 0xE7, 0x2B, 0x79, 0x3A, 0xBB, 0xBD, 0x25, 0x45, 0x34,
|
||||||
|
0xE1, 0x39, 0x88, 0xA0, 0x4B, 0x79, 0xCE, 0x51, 0xB7, 0xC9, 0x32, 0x2F, 0xC9, 0xBA, 0x1F, 0xA0,
|
||||||
|
0x7E, 0xC8, 0x1C, 0xE0, 0xF6, 0xD1, 0xC7, 0xBC, 0xC3, 0x11, 0x01, 0xCF, 0xC7, 0xAA, 0xE8, 0xA1,
|
||||||
|
0x49, 0x87, 0x90, 0x1A, 0x9A, 0xBD, 0x4F, 0xD4, 0xCB, 0xDE, 0xDA, 0xD0, 0x38, 0xDA, 0x0A, 0xD5,
|
||||||
|
0x2A, 0xC3, 0x39, 0x03, 0x67, 0x36, 0x91, 0xC6, 0x7C, 0x31, 0xF9, 0x8D, 0x4F, 0x2B, 0xB1, 0xE0,
|
||||||
|
0xB7, 0x59, 0x9E, 0xF7, 0x3A, 0xBB, 0xF5, 0x43, 0xFF, 0x19, 0xD5, 0xF2, 0x9C, 0x45, 0xD9, 0x27,
|
||||||
|
0x2C, 0x22, 0x97, 0xBF, 0x2A, 0xFC, 0xE6, 0x15, 0x71, 0xFC, 0x91, 0x0F, 0x25, 0x15, 0x94, 0x9B,
|
||||||
|
0x61, 0x93, 0xE5, 0xFA, 0xEB, 0x9C, 0xB6, 0xCE, 0x59, 0x64, 0xA8, 0xC2, 0xD1, 0xA8, 0xBA, 0x12,
|
||||||
|
0x5E, 0x07, 0xC1, 0xB6, 0x0C, 0x6A, 0x05, 0xE3, 0x65, 0x50, 0xD2, 0x10, 0x42, 0xA4, 0x03, 0xCB,
|
||||||
|
0x0E, 0x6E, 0xEC, 0xE0, 0x3B, 0xDB, 0x98, 0x16, 0xBE, 0xA0, 0x98, 0x4C, 0x64, 0xE9, 0x78, 0x32,
|
||||||
|
0x32, 0x95, 0x1F, 0x9F, 0xDF, 0x92, 0xD3, 0xE0, 0x2B, 0x34, 0xA0, 0xD3, 0x1E, 0xF2, 0x71, 0x89,
|
||||||
|
0x41, 0x74, 0x0A, 0x1B, 0x8C, 0x34, 0xA3, 0x4B, 0x20, 0x71, 0xBE, 0xC5, 0xD8, 0x32, 0x76, 0xC3,
|
||||||
|
0x8D, 0x9F, 0x35, 0xDF, 0x2E, 0x2F, 0x99, 0x9B, 0x47, 0x6F, 0x0B, 0xE6, 0x1D, 0xF1, 0xE3, 0x0F,
|
||||||
|
0x54, 0xDA, 0x4C, 0xE5, 0x91, 0xD8, 0xDA, 0x1E, 0xCF, 0x79, 0x62, 0xCE, 0x6F, 0x7E, 0x3E, 0xCD,
|
||||||
|
0x66, 0xB1, 0x18, 0x16, 0x05, 0x1D, 0x2C, 0xFD, 0xC5, 0xD2, 0x8F, 0x84, 0x99, 0x22, 0xFB, 0xF6,
|
||||||
|
0x57, 0xF3, 0x23, 0xF5, 0x23, 0x76, 0x32, 0xA6, 0x31, 0x35, 0xA8, 0x93, 0x02, 0xCD, 0xCC, 0x56,
|
||||||
|
0x62, 0x81, 0xF0, 0xAC, 0xB5, 0xEB, 0x75, 0x5A, 0x97, 0x36, 0x16, 0x6E, 0xCC, 0x73, 0xD2, 0x88,
|
||||||
|
0x92, 0x62, 0x96, 0xDE, 0xD0, 0x49, 0xB9, 0x81, 0x1B, 0x90, 0x50, 0x4C, 0x14, 0x56, 0xC6, 0x71,
|
||||||
|
0xBD, 0xC7, 0xC6, 0xE6, 0x0A, 0x14, 0x7A, 0x32, 0x06, 0xD0, 0xE1, 0x45, 0x9A, 0x7B, 0xF2, 0xC3,
|
||||||
|
0xFD, 0x53, 0xAA, 0xC9, 0x00, 0x0F, 0xA8, 0x62, 0xE2, 0xBF, 0x25, 0xBB, 0xF6, 0xD2, 0xBD, 0x35,
|
||||||
|
0x05, 0x69, 0x12, 0x71, 0x22, 0x02, 0x04, 0xB2, 0x7C, 0xCF, 0xCB, 0xB6, 0x2B, 0x9C, 0x76, 0xCD,
|
||||||
|
0xC0, 0x3E, 0x11, 0x53, 0xD3, 0xE3, 0x40, 0x16, 0x60, 0xBD, 0xAB, 0x38, 0xF0, 0xAD, 0x47, 0x25,
|
||||||
|
0x9C, 0x20, 0x38, 0xBA, 0x76, 0xCE, 0x46, 0xF7, 0xC5, 0xA1, 0xAF, 0x77, 0x60, 0x60, 0x75, 0x20,
|
||||||
|
0x4E, 0xFE, 0xCB, 0x85, 0xD8, 0x8D, 0xE8, 0x8A, 0xB0, 0xF9, 0xAA, 0x7A, 0x7E, 0xAA, 0xF9, 0x4C,
|
||||||
|
0x5C, 0xC2, 0x48, 0x19, 0x8C, 0x8A, 0xFB, 0x02, 0xE4, 0x6A, 0xC3, 0x01, 0xF9, 0xE1, 0xEB, 0xD6,
|
||||||
|
0x69, 0xF8, 0xD4, 0x90, 0xA0, 0xDE, 0x5C, 0xA6, 0x2D, 0x25, 0x09, 0x3F, 0x9F, 0xE6, 0x08, 0xC2,
|
||||||
|
0x32, 0x61, 0x4E, 0xB7, 0x5B, 0xE2, 0x77, 0xCE, 0xE3, 0xDF, 0x8F, 0x57, 0xE6, 0x72, 0xC3, 0x3A
|
||||||
|
};
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public Blowfish(byte[] key)
|
||||||
|
{
|
||||||
|
initializeBlowfish(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
uint xl = (uint)((data[i + 0]) | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24));
|
||||||
|
uint xr = (uint)((data[i + 4]) | (data[i + 5] << 8) | (data[i + 6] << 16) | (data[i + 7] << 24));
|
||||||
|
blowfish_encipher(ref xl, ref xr);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
uint xl = (uint)((data[i + 0]) | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24));
|
||||||
|
uint xr = (uint)((data[i + 4]) | (data[i + 5] << 8) | (data[i + 6] << 16) | (data[i + 7] << 24));
|
||||||
|
blowfish_decipher(ref xl, ref xr);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UInt32 F(UInt32 x)
|
||||||
|
{
|
||||||
|
UInt16 a;
|
||||||
|
UInt16 b;
|
||||||
|
UInt16 c;
|
||||||
|
UInt16 d;
|
||||||
|
UInt32 y;
|
||||||
|
|
||||||
|
d = (UInt16)(x & 0x00FF);
|
||||||
|
x >>= 8;
|
||||||
|
c = (UInt16)(x & 0x00FF);
|
||||||
|
x >>= 8;
|
||||||
|
b = (UInt16)(x & 0x00FF);
|
||||||
|
x >>= 8;
|
||||||
|
a = (UInt16)(x & 0x00FF);
|
||||||
|
//y = ((S[0][a] + S[1][b]) ^ S[2][c]) + S[3][d];
|
||||||
|
y = S[0,a] + S[1,b];
|
||||||
|
y = y ^ S[2,c];
|
||||||
|
y = y + S[3,d];
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void blowfish_encipher(ref UInt32 xl, ref UInt32 xr)
|
||||||
|
{
|
||||||
|
UInt32 temp;
|
||||||
|
Int32 i;
|
||||||
|
|
||||||
|
for (i = 0; i < N; ++i) {
|
||||||
|
xl = xl ^ P[i];
|
||||||
|
xr = F(xl) ^ xr;
|
||||||
|
|
||||||
|
temp = xl;
|
||||||
|
xl = xr;
|
||||||
|
xr = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp = xl;
|
||||||
|
xl = xr;
|
||||||
|
xr = temp;
|
||||||
|
|
||||||
|
xr = xr ^ P[N];
|
||||||
|
xl = xl ^ P[N + 1];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void blowfish_decipher(ref UInt32 xl, ref UInt32 xr)
|
||||||
|
{
|
||||||
|
UInt32 temp;
|
||||||
|
Int32 i;
|
||||||
|
|
||||||
|
for (i = N + 1; i > 1; --i) {
|
||||||
|
xl = xl ^ P[i];
|
||||||
|
xr = F(xl) ^ xr;
|
||||||
|
|
||||||
|
/* Exchange xl and xr */
|
||||||
|
temp = xl;
|
||||||
|
xl = xr;
|
||||||
|
xr = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exchange xl and xr */
|
||||||
|
temp = xl;
|
||||||
|
xl = xr;
|
||||||
|
xr = temp;
|
||||||
|
|
||||||
|
xr = xr ^ P[1];
|
||||||
|
xl = xl ^ P[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int initializeBlowfish(byte [] key)
|
||||||
|
{
|
||||||
|
Int16 i;
|
||||||
|
Int16 j;
|
||||||
|
Int16 k;
|
||||||
|
|
||||||
|
int data;
|
||||||
|
uint datal;
|
||||||
|
uint datar;
|
||||||
|
|
||||||
|
Buffer.BlockCopy(P_values, 0, P, 0, P_values.Length);
|
||||||
|
Buffer.BlockCopy(S_values, 0, S, 0, S_values.Length);
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
for (i = 0; i < N + 2; ++i)
|
||||||
|
{
|
||||||
|
data = 0x00000000;
|
||||||
|
for (k = 0; k < 4; ++k)
|
||||||
|
{
|
||||||
|
data = (data << 8) | (SByte)key[j];
|
||||||
|
j = (short)(j + 1);
|
||||||
|
if (j >= key.Length)
|
||||||
|
{
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
P[i] = P[i] ^ (uint)data;
|
||||||
|
}
|
||||||
|
|
||||||
|
datal = 0x00000000;
|
||||||
|
datar = 0x00000000;
|
||||||
|
|
||||||
|
for (i = 0; i < N + 2; i += 2)
|
||||||
|
{
|
||||||
|
blowfish_encipher(ref datal, ref datar);
|
||||||
|
|
||||||
|
P[i] = datal;
|
||||||
|
P[i + 1] = datar;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
for (j = 0; j < 256; j += 2)
|
||||||
|
{
|
||||||
|
blowfish_encipher(ref datal, ref datar);
|
||||||
|
S[i,j] = datal;
|
||||||
|
S[i,j + 1] = datar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
56
FFXIVClassic_Lobby_Server/common/Log.cs
Normal file
56
FFXIVClassic_Lobby_Server/common/Log.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.common
|
||||||
|
{
|
||||||
|
class Log
|
||||||
|
{
|
||||||
|
public static void error(String message)
|
||||||
|
{
|
||||||
|
Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm"));
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.Write("[ERROR] ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Gray ;
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void debug(String message)
|
||||||
|
{
|
||||||
|
Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm"));
|
||||||
|
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||||
|
Console.Write("[DEBUG] ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void info(String message)
|
||||||
|
{
|
||||||
|
Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm"));
|
||||||
|
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||||
|
Console.Write("[INFO] ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void database(String message)
|
||||||
|
{
|
||||||
|
Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm"));
|
||||||
|
Console.ForegroundColor = ConsoleColor.Magenta;
|
||||||
|
Console.Write("[SQL] ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void conn(String message)
|
||||||
|
{
|
||||||
|
Console.Write("[{0}]", DateTime.Now.ToString("dd/MMM HH:mm"));
|
||||||
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
|
Console.Write("[CONN] ");
|
||||||
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
|
Console.WriteLine(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
533
FFXIVClassic_Lobby_Server/common/STA_INIFile.cs
Normal file
533
FFXIVClassic_Lobby_Server/common/STA_INIFile.cs
Normal file
@ -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<string, Dictionary<string, string>> m_Sections = new Dictionary<string, Dictionary<string, string>>();
|
||||||
|
private Dictionary<string, Dictionary<string, string>> m_Modified = new Dictionary<string, Dictionary<string, string>>();
|
||||||
|
|
||||||
|
// *** 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<string, string> 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<string, string>();
|
||||||
|
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<string, string> 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<string, Dictionary<string, string>> 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<string, string> 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<string, string> 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<string, string> Section;
|
||||||
|
if (!m_Sections.TryGetValue(SectionName, out Section))
|
||||||
|
{
|
||||||
|
// *** If it doesn't, add it ***
|
||||||
|
Section = new Dictionary<string, string>();
|
||||||
|
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<string, string>();
|
||||||
|
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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
183
FFXIVClassic_Lobby_Server/common/Utils.cs
Normal file
183
FFXIVClassic_Lobby_Server/common/Utils.cs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt32 UnixTimeStampUTC()
|
||||||
|
{
|
||||||
|
UInt32 unixTimeStamp;
|
||||||
|
DateTime currentTime = DateTime.Now;
|
||||||
|
DateTime zuluTime = currentTime.ToUniversalTime();
|
||||||
|
DateTime unixEpoch = new DateTime(1970, 1, 1);
|
||||||
|
unixTimeStamp = (UInt32)(zuluTime.Subtract(unixEpoch)).TotalSeconds;
|
||||||
|
|
||||||
|
return unixTimeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt64 MilisUnixTimeStampUTC()
|
||||||
|
{
|
||||||
|
UInt64 unixTimeStamp;
|
||||||
|
DateTime currentTime = DateTime.Now;
|
||||||
|
DateTime zuluTime = currentTime.ToUniversalTime();
|
||||||
|
DateTime unixEpoch = new DateTime(1970, 1, 1);
|
||||||
|
unixTimeStamp = (UInt64)(zuluTime.Subtract(unixEpoch)).TotalMilliseconds;
|
||||||
|
|
||||||
|
return unixTimeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong swapEndian(ulong input)
|
||||||
|
{
|
||||||
|
return ((0x00000000000000FF) & (input >> 56) |
|
||||||
|
(0x000000000000FF00) & (input >> 40) |
|
||||||
|
(0x0000000000FF0000) & (input >> 24) |
|
||||||
|
(0x00000000FF000000) & (input >> 8) |
|
||||||
|
(0x000000FF00000000) & (input << 8) |
|
||||||
|
(0x0000FF0000000000) & (input << 24) |
|
||||||
|
(0x00FF000000000000) & (input << 40) |
|
||||||
|
(0xFF00000000000000) & (input << 56));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint swapEndian(uint input)
|
||||||
|
{
|
||||||
|
return ((input >> 24) & 0xff) |
|
||||||
|
((input << 8) & 0xff0000) |
|
||||||
|
((input >> 8) & 0xff00) |
|
||||||
|
((input << 24) & 0xff000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int swapEndian(int input)
|
||||||
|
{
|
||||||
|
uint inputAsUint = (uint)input;
|
||||||
|
|
||||||
|
input = (int)
|
||||||
|
(((inputAsUint >> 24) & 0xff) |
|
||||||
|
((inputAsUint << 8) & 0xff0000) |
|
||||||
|
((inputAsUint >> 8) & 0xff00) |
|
||||||
|
((inputAsUint << 24) & 0xff000000));
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint MurmurHash2(string key, uint seed)
|
||||||
|
{
|
||||||
|
// 'm' and 'r' are mixing constants generated offline.
|
||||||
|
// They're not really 'magic', they just happen to work well.
|
||||||
|
|
||||||
|
byte[] data = Encoding.ASCII.GetBytes(key);
|
||||||
|
const uint m = 0x5bd1e995;
|
||||||
|
const int r = 24;
|
||||||
|
int len = key.Length;
|
||||||
|
int dataIndex = len - 4;
|
||||||
|
|
||||||
|
// Initialize the hash to a 'random' value
|
||||||
|
|
||||||
|
uint h = seed ^ (uint)len;
|
||||||
|
|
||||||
|
// Mix 4 bytes at a time into the hash
|
||||||
|
|
||||||
|
|
||||||
|
while (len >= 4)
|
||||||
|
{
|
||||||
|
h *= m;
|
||||||
|
|
||||||
|
uint k = (uint)BitConverter.ToInt32(data, dataIndex);
|
||||||
|
k = ((k >> 24) & 0xff) | // move byte 3 to byte 0
|
||||||
|
((k << 8) & 0xff0000) | // move byte 1 to byte 2
|
||||||
|
((k >> 8) & 0xff00) | // move byte 2 to byte 1
|
||||||
|
((k << 24) & 0xff000000); // byte 0 to byte 3
|
||||||
|
|
||||||
|
k *= m;
|
||||||
|
k ^= k >> r;
|
||||||
|
k *= m;
|
||||||
|
|
||||||
|
h ^= k;
|
||||||
|
|
||||||
|
dataIndex -= 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the last few bytes of the input array
|
||||||
|
switch (len)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
h ^= (uint)data[0] << 16; goto case 2;
|
||||||
|
case 2:
|
||||||
|
h ^= (uint)data[len - 2] << 8; goto case 1;
|
||||||
|
case 1:
|
||||||
|
h ^= data[len - 1];
|
||||||
|
h *= m;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do a few final mixes of the hash to ensure the last few
|
||||||
|
// bytes are well-incorporated.
|
||||||
|
|
||||||
|
h ^= h >> 13;
|
||||||
|
h *= m;
|
||||||
|
h ^= h >> 15;
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] ConvertBoolArrayToBinaryStream(bool[] array)
|
||||||
|
{
|
||||||
|
byte[] data = new byte[(array.Length / 8) + (array.Length % 8 != 0 ? 1 : 0)];
|
||||||
|
|
||||||
|
int dataCounter = 0;
|
||||||
|
for (int i = 0; i < array.Length; i += 8)
|
||||||
|
{
|
||||||
|
for (int bitCount = 0; bitCount < 8; bitCount++)
|
||||||
|
{
|
||||||
|
if (i + bitCount >= array.Length)
|
||||||
|
break;
|
||||||
|
data[dataCounter] = (byte)(((array[i + bitCount] ? 1 : 0) << 7 - bitCount) | data[dataCounter]);
|
||||||
|
}
|
||||||
|
dataCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
FFXIVClassic_Lobby_Server/dataobjects/Account.cs
Normal file
14
FFXIVClassic_Lobby_Server/dataobjects/Account.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.dataobjects
|
||||||
|
{
|
||||||
|
class Account
|
||||||
|
{
|
||||||
|
public UInt32 id;
|
||||||
|
public string name;
|
||||||
|
}
|
||||||
|
}
|
49
FFXIVClassic_Lobby_Server/dataobjects/Appearance.cs
Normal file
49
FFXIVClassic_Lobby_Server/dataobjects/Appearance.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.dataobjects
|
||||||
|
{
|
||||||
|
class Appearance
|
||||||
|
{
|
||||||
|
////////////
|
||||||
|
//Chara Info
|
||||||
|
public byte size = 0;
|
||||||
|
public byte voice = 0;
|
||||||
|
public ushort skinColor = 0;
|
||||||
|
|
||||||
|
public ushort hairStyle = 0;
|
||||||
|
public ushort hairColor = 0;
|
||||||
|
public ushort hairHighlightColor = 0;
|
||||||
|
public ushort eyeColor = 0;
|
||||||
|
public byte characteristicsColor = 0;
|
||||||
|
|
||||||
|
public byte faceType = 0;
|
||||||
|
public byte faceEyebrows = 0;
|
||||||
|
public byte faceEyeShape = 0;
|
||||||
|
public byte faceIrisSize = 0;
|
||||||
|
public byte faceNose = 0;
|
||||||
|
public byte faceMouth = 0;
|
||||||
|
public byte faceFeatures = 0;
|
||||||
|
public byte characteristics = 0;
|
||||||
|
public byte ears = 0;
|
||||||
|
|
||||||
|
public uint mainHand = 0;
|
||||||
|
public uint offHand = 0;
|
||||||
|
|
||||||
|
public uint head = 0;
|
||||||
|
public uint body = 0;
|
||||||
|
public uint legs = 0;
|
||||||
|
public uint hands = 0;
|
||||||
|
public uint feet = 0;
|
||||||
|
public uint waist = 0;
|
||||||
|
public uint rightEar = 0;
|
||||||
|
public uint leftEar = 0;
|
||||||
|
public uint rightFinger = 0;
|
||||||
|
public uint leftFinger = 0;
|
||||||
|
//Chara Info
|
||||||
|
////////////
|
||||||
|
}
|
||||||
|
}
|
288
FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs
Normal file
288
FFXIVClassic_Lobby_Server/dataobjects/CharaInfo.cs
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FFXIVClassic_Lobby_Server.common;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.dataobjects
|
||||||
|
{
|
||||||
|
class CharaInfo
|
||||||
|
{
|
||||||
|
public Appearance appearance;
|
||||||
|
|
||||||
|
public struct FaceInfo
|
||||||
|
{
|
||||||
|
[BitfieldLength(5)]
|
||||||
|
public uint characteristics;
|
||||||
|
[BitfieldLength(3)]
|
||||||
|
public uint characteristicsColor;
|
||||||
|
[BitfieldLength(6)]
|
||||||
|
public uint type;
|
||||||
|
[BitfieldLength(2)]
|
||||||
|
public uint ears;
|
||||||
|
[BitfieldLength(2)]
|
||||||
|
public uint mouth;
|
||||||
|
[BitfieldLength(2)]
|
||||||
|
public uint features;
|
||||||
|
[BitfieldLength(3)]
|
||||||
|
public uint nose;
|
||||||
|
[BitfieldLength(3)]
|
||||||
|
public uint eyeShape;
|
||||||
|
[BitfieldLength(1)]
|
||||||
|
public uint irisSize;
|
||||||
|
[BitfieldLength(3)]
|
||||||
|
public uint eyebrows;
|
||||||
|
[BitfieldLength(2)]
|
||||||
|
public uint unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint guardian = 0;
|
||||||
|
public uint birthMonth = 0;
|
||||||
|
public uint birthDay = 0;
|
||||||
|
public uint currentClass = 0;
|
||||||
|
public uint currentJob = 0;
|
||||||
|
public uint initialTown = 0;
|
||||||
|
public uint tribe = 0;
|
||||||
|
|
||||||
|
public ushort zoneId;
|
||||||
|
public float x, y, z, rot;
|
||||||
|
|
||||||
|
public uint currentLevel = 1;
|
||||||
|
|
||||||
|
public uint weapon1;
|
||||||
|
public uint weapon2;
|
||||||
|
public uint head;
|
||||||
|
public uint body;
|
||||||
|
public uint hands;
|
||||||
|
public uint legs;
|
||||||
|
public uint feet;
|
||||||
|
public uint belt;
|
||||||
|
|
||||||
|
public static CharaInfo getFromNewCharRequest(String encoded)
|
||||||
|
{
|
||||||
|
byte[] data = Convert.FromBase64String(encoded.Replace('-', '+').Replace('_', '/'));
|
||||||
|
|
||||||
|
CharaInfo info = new CharaInfo();
|
||||||
|
Appearance appearance = new Appearance();
|
||||||
|
|
||||||
|
using (MemoryStream stream = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
using (BinaryReader reader = new BinaryReader(stream))
|
||||||
|
{
|
||||||
|
uint version = reader.ReadUInt32();
|
||||||
|
uint unknown1 = reader.ReadUInt32();
|
||||||
|
info.tribe = reader.ReadByte();
|
||||||
|
appearance.size = reader.ReadByte();
|
||||||
|
appearance.hairStyle = reader.ReadUInt16();
|
||||||
|
appearance.hairHighlightColor = reader.ReadUInt16();
|
||||||
|
appearance.faceType = reader.ReadByte();
|
||||||
|
appearance.characteristics = reader.ReadByte();
|
||||||
|
appearance.characteristicsColor = reader.ReadByte();
|
||||||
|
|
||||||
|
reader.ReadUInt32();
|
||||||
|
|
||||||
|
appearance.faceEyebrows = reader.ReadByte();
|
||||||
|
appearance.faceIrisSize = reader.ReadByte();
|
||||||
|
appearance.faceEyeShape = reader.ReadByte();
|
||||||
|
appearance.faceNose = reader.ReadByte();
|
||||||
|
appearance.faceFeatures = reader.ReadByte();
|
||||||
|
appearance.faceMouth = reader.ReadByte();
|
||||||
|
appearance.ears = reader.ReadByte();
|
||||||
|
appearance.hairColor = reader.ReadUInt16();
|
||||||
|
|
||||||
|
reader.ReadUInt32();
|
||||||
|
|
||||||
|
appearance.skinColor = reader.ReadUInt16();
|
||||||
|
appearance.eyeColor = reader.ReadUInt16();
|
||||||
|
|
||||||
|
appearance.voice = reader.ReadByte();
|
||||||
|
info.guardian = reader.ReadByte();
|
||||||
|
info.birthMonth = reader.ReadByte();
|
||||||
|
info.birthDay = reader.ReadByte();
|
||||||
|
info.currentClass = reader.ReadUInt16();
|
||||||
|
|
||||||
|
reader.ReadUInt32();
|
||||||
|
reader.ReadUInt32();
|
||||||
|
reader.ReadUInt32();
|
||||||
|
|
||||||
|
reader.BaseStream.Seek(0x10, SeekOrigin.Current);
|
||||||
|
|
||||||
|
info.initialTown = reader.ReadByte();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info.appearance = appearance;
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildForCharaList(Character chara, Appearance appearance)
|
||||||
|
{
|
||||||
|
byte[] data;
|
||||||
|
|
||||||
|
using (MemoryStream stream = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(stream))
|
||||||
|
{
|
||||||
|
//Build faceinfo for later
|
||||||
|
FaceInfo faceInfo = new FaceInfo();
|
||||||
|
faceInfo.characteristics = appearance.characteristics;
|
||||||
|
faceInfo.characteristicsColor = appearance.characteristicsColor;
|
||||||
|
faceInfo.type = appearance.faceType;
|
||||||
|
faceInfo.ears = appearance.ears;
|
||||||
|
faceInfo.features = appearance.faceFeatures;
|
||||||
|
faceInfo.eyebrows = appearance.faceEyebrows;
|
||||||
|
faceInfo.eyeShape = appearance.faceEyeShape;
|
||||||
|
faceInfo.irisSize = appearance.faceIrisSize;
|
||||||
|
faceInfo.mouth = appearance.faceMouth;
|
||||||
|
faceInfo.nose = appearance.faceNose;
|
||||||
|
|
||||||
|
|
||||||
|
string location1 = "prv0Inn01\0";
|
||||||
|
string location2 = "defaultTerritory\0";
|
||||||
|
|
||||||
|
writer.Write((UInt32)0x000004c0);
|
||||||
|
writer.Write((UInt32)0x232327ea);
|
||||||
|
writer.Write((UInt32)System.Text.Encoding.UTF8.GetBytes(chara.name + '\0').Length);
|
||||||
|
writer.Write(System.Text.Encoding.UTF8.GetBytes(chara.name + '\0'));
|
||||||
|
writer.Write((UInt32)0x1c);
|
||||||
|
writer.Write((UInt32)0x04);
|
||||||
|
writer.Write((UInt32)getTribeModel(chara.tribe));
|
||||||
|
writer.Write((UInt32)appearance.size);
|
||||||
|
uint colorVal = appearance.skinColor | (uint)(appearance.hairColor << 10) | (uint)(appearance.eyeColor << 20);
|
||||||
|
writer.Write((UInt32)colorVal);
|
||||||
|
|
||||||
|
var bitfield = PrimitiveConversion.ToUInt32(faceInfo);
|
||||||
|
|
||||||
|
writer.Write((UInt32)bitfield); //FACE, Figure this out!
|
||||||
|
uint hairVal = appearance.hairHighlightColor | (uint)(appearance.hairStyle << 10) | (uint)(appearance.characteristicsColor << 20);
|
||||||
|
writer.Write((UInt32)hairVal);
|
||||||
|
writer.Write((UInt32)appearance.voice);
|
||||||
|
writer.Write((UInt32)appearance.mainHand);
|
||||||
|
writer.Write((UInt32)appearance.offHand);
|
||||||
|
|
||||||
|
writer.Write((UInt32)0);
|
||||||
|
writer.Write((UInt32)0);
|
||||||
|
writer.Write((UInt32)0);
|
||||||
|
writer.Write((UInt32)0);
|
||||||
|
writer.Write((UInt32)0);
|
||||||
|
|
||||||
|
writer.Write((UInt32)appearance.head);
|
||||||
|
writer.Write((UInt32)appearance.body);
|
||||||
|
writer.Write((UInt32)appearance.legs);
|
||||||
|
writer.Write((UInt32)appearance.hands);
|
||||||
|
writer.Write((UInt32)appearance.feet);
|
||||||
|
writer.Write((UInt32)appearance.waist);
|
||||||
|
|
||||||
|
writer.Write((UInt32)0);
|
||||||
|
|
||||||
|
writer.Write((UInt32)appearance.rightEar);
|
||||||
|
writer.Write((UInt32)appearance.leftEar);
|
||||||
|
|
||||||
|
writer.Write((UInt32)0);
|
||||||
|
writer.Write((UInt32)0);
|
||||||
|
|
||||||
|
writer.Write((UInt32)appearance.rightFinger);
|
||||||
|
writer.Write((UInt32)appearance.leftFinger);
|
||||||
|
|
||||||
|
for (int i = 0; i < 0x8; i++)
|
||||||
|
writer.Write((byte)0);
|
||||||
|
|
||||||
|
writer.Write((UInt32)1);
|
||||||
|
writer.Write((UInt32)1);
|
||||||
|
|
||||||
|
writer.Write((byte)chara.currentClass);
|
||||||
|
writer.Write((UInt16)chara.currentLevel);
|
||||||
|
writer.Write((byte)chara.currentJob);
|
||||||
|
writer.Write((UInt16)1);
|
||||||
|
writer.Write((byte)chara.tribe);
|
||||||
|
|
||||||
|
writer.Write((UInt32)0xe22222aa);
|
||||||
|
|
||||||
|
writer.Write((UInt32)System.Text.Encoding.UTF8.GetBytes(location1).Length);
|
||||||
|
writer.Write(System.Text.Encoding.UTF8.GetBytes(location1));
|
||||||
|
writer.Write((UInt32)System.Text.Encoding.UTF8.GetBytes(location2).Length);
|
||||||
|
writer.Write(System.Text.Encoding.UTF8.GetBytes(location2));
|
||||||
|
|
||||||
|
writer.Write((byte)chara.guardian);
|
||||||
|
writer.Write((byte)chara.birthMonth);
|
||||||
|
writer.Write((byte)chara.birthDay);
|
||||||
|
|
||||||
|
writer.Write((UInt16)0x17);
|
||||||
|
writer.Write((UInt32)4);
|
||||||
|
writer.Write((UInt32)4);
|
||||||
|
|
||||||
|
writer.BaseStream.Seek(0x10, SeekOrigin.Current);
|
||||||
|
|
||||||
|
writer.Write((UInt32)chara.initialTown);
|
||||||
|
writer.Write((UInt32)chara.initialTown);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = stream.GetBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String debug()
|
||||||
|
{
|
||||||
|
byte[] bytes = File.ReadAllBytes("./packets/charaappearance.bin");
|
||||||
|
|
||||||
|
Console.WriteLine(Utils.ByteArrayToHex(bytes));
|
||||||
|
|
||||||
|
return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt32 getTribeModel(byte tribe)
|
||||||
|
{
|
||||||
|
switch (tribe)
|
||||||
|
{
|
||||||
|
//Hyur Midlander Male
|
||||||
|
case 1:
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
//Hyur Midlander Female
|
||||||
|
case 2:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
//Elezen Male
|
||||||
|
case 4:
|
||||||
|
case 6:
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
//Elezen Female
|
||||||
|
case 5:
|
||||||
|
case 7:
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
//Lalafell Male
|
||||||
|
case 8:
|
||||||
|
case 10:
|
||||||
|
return 5;
|
||||||
|
|
||||||
|
//Lalafell Female
|
||||||
|
case 9:
|
||||||
|
case 11:
|
||||||
|
return 6;
|
||||||
|
|
||||||
|
//Miqo'te Female
|
||||||
|
case 12:
|
||||||
|
case 13:
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
//Roegadyn Male
|
||||||
|
case 14:
|
||||||
|
case 15:
|
||||||
|
return 7;
|
||||||
|
|
||||||
|
//Hyur Highlander Male
|
||||||
|
case 3:
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
FFXIVClassic_Lobby_Server/dataobjects/Character.cs
Normal file
48
FFXIVClassic_Lobby_Server/dataobjects/Character.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FFXIVClassic_Lobby_Server.common;
|
||||||
|
using FFXIVClassic_Lobby_Server.dataobjects;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server
|
||||||
|
{
|
||||||
|
class Character
|
||||||
|
{
|
||||||
|
public uint id;
|
||||||
|
public ushort slot;
|
||||||
|
public ushort serverId;
|
||||||
|
public string name;
|
||||||
|
public ushort state;
|
||||||
|
public string charaInfo;
|
||||||
|
public bool isLegacy;
|
||||||
|
public bool doRename;
|
||||||
|
public uint currentZoneId;
|
||||||
|
|
||||||
|
public byte guardian;
|
||||||
|
public byte birthMonth;
|
||||||
|
public byte birthDay;
|
||||||
|
public uint currentClass = 3;
|
||||||
|
public uint currentJob = 0;
|
||||||
|
public byte initialTown;
|
||||||
|
public byte tribe;
|
||||||
|
|
||||||
|
public uint currentLevel = 1;
|
||||||
|
|
||||||
|
public static CharaInfo 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------------------");
|
||||||
|
|
||||||
|
CharaInfo chara = new CharaInfo();
|
||||||
|
|
||||||
|
return chara;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
FFXIVClassic_Lobby_Server/dataobjects/Retainer.cs
Normal file
18
FFXIVClassic_Lobby_Server/dataobjects/Retainer.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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 Retainer
|
||||||
|
{
|
||||||
|
public uint id;
|
||||||
|
public uint characterId;
|
||||||
|
public string name;
|
||||||
|
public ushort slot;
|
||||||
|
public bool doRename;
|
||||||
|
}
|
||||||
|
}
|
19
FFXIVClassic_Lobby_Server/dataobjects/World.cs
Normal file
19
FFXIVClassic_Lobby_Server/dataobjects/World.cs
Normal 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 listPosition;
|
||||||
|
public ushort population;
|
||||||
|
public string name;
|
||||||
|
public bool isActive;
|
||||||
|
}
|
||||||
|
}
|
7
FFXIVClassic_Lobby_Server/packages.config
Normal file
7
FFXIVClassic_Lobby_Server/packages.config
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Cyotek.CircularBuffer" version="1.0.0.0" targetFramework="net45" />
|
||||||
|
<package id="Dapper" version="1.42" targetFramework="net45" />
|
||||||
|
<package id="MySql.Data" version="6.9.7" targetFramework="net45" />
|
||||||
|
<package id="Newtonsoft.Json" version="7.0.1" targetFramework="net45" />
|
||||||
|
</packages>
|
350
FFXIVClassic_Lobby_Server/packets/BasePacket.cs
Normal file
350
FFXIVClassic_Lobby_Server/packets/BasePacket.cs
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
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 connectionType;
|
||||||
|
public ushort packetSize;
|
||||||
|
public ushort numSubpackets;
|
||||||
|
public ulong timestamp; //Miliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BasePacket
|
||||||
|
{
|
||||||
|
|
||||||
|
public const int TYPE_ZONE = 1;
|
||||||
|
public const int TYPE_CHAT = 2;
|
||||||
|
public const int BASEPACKET_SIZE = 0x10;
|
||||||
|
|
||||||
|
public BasePacketHeader header;
|
||||||
|
public byte[] data;
|
||||||
|
|
||||||
|
//Loads a sniffed packet from a file
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (packetSize - BASEPACKET_SIZE != 0)
|
||||||
|
{
|
||||||
|
data = new byte[packetSize - BASEPACKET_SIZE];
|
||||||
|
Array.Copy(bytes, BASEPACKET_SIZE, data, 0, packetSize - BASEPACKET_SIZE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
data = new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Loads a sniffed packet from a byte array
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Replaces all instances of the sniffed actorID with the given one
|
||||||
|
public void replaceActorID(uint actorID)
|
||||||
|
{
|
||||||
|
using (MemoryStream mem = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
using (BinaryWriter binWriter = new BinaryWriter(mem))
|
||||||
|
{
|
||||||
|
using (BinaryReader binreader = new BinaryReader(mem))
|
||||||
|
{
|
||||||
|
while (binreader.BaseStream.Position + 4 < data.Length)
|
||||||
|
{
|
||||||
|
uint read = binreader.ReadUInt32();
|
||||||
|
if (read == 0x029B2941 || read == 0x02977DC7 || read == 0x0297D2C8 || read == 0x0230d573 || read == 0x23317df || read == 0x23344a3 || read == 0x1730bdb) //Original ID
|
||||||
|
{
|
||||||
|
binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin);
|
||||||
|
binWriter.Write(actorID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Replaces all instances of the sniffed actorID with the given one
|
||||||
|
public void replaceActorID(uint fromActorID, uint actorID)
|
||||||
|
{
|
||||||
|
using (MemoryStream mem = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
using (BinaryWriter binWriter = new BinaryWriter(mem))
|
||||||
|
{
|
||||||
|
using (BinaryReader binreader = new BinaryReader(mem))
|
||||||
|
{
|
||||||
|
while (binreader.BaseStream.Position + 4 < data.Length)
|
||||||
|
{
|
||||||
|
uint read = binreader.ReadUInt32();
|
||||||
|
if (read == fromActorID) //Original ID
|
||||||
|
{
|
||||||
|
binWriter.BaseStream.Seek(binreader.BaseStream.Position - 0x4, SeekOrigin.Begin);
|
||||||
|
binWriter.Write(actorID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Utility Functions
|
||||||
|
public static 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;
|
||||||
|
header.timestamp = Utils.MilisUnixTimeStampUTC();
|
||||||
|
|
||||||
|
//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);
|
||||||
|
offset += (ushort)subpacketData.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Assert(data != null && offset == data.Length && header.packetSize == 0x10 + offset);
|
||||||
|
|
||||||
|
BasePacket packet = new BasePacket(header, data);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BasePacket createPacket(SubPacket subpacket, 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)1;
|
||||||
|
header.packetSize = BASEPACKET_SIZE;
|
||||||
|
header.timestamp = Utils.MilisUnixTimeStampUTC();
|
||||||
|
|
||||||
|
//Get packet size
|
||||||
|
header.packetSize += subpacket.header.subpacketSize;
|
||||||
|
|
||||||
|
data = new byte[header.packetSize - 0x10];
|
||||||
|
|
||||||
|
//Add Subpackets
|
||||||
|
byte[] subpacketData = subpacket.getBytes();
|
||||||
|
Array.Copy(subpacketData, 0, data, 0, subpacketData.Length);
|
||||||
|
|
||||||
|
Debug.Assert(data != null);
|
||||||
|
|
||||||
|
BasePacket packet = new BasePacket(header, data);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BasePacket createPacket(byte[] data, bool isAuthed, bool isEncrypted)
|
||||||
|
{
|
||||||
|
|
||||||
|
Debug.Assert(data != null);
|
||||||
|
|
||||||
|
//Create Header
|
||||||
|
BasePacketHeader header = new BasePacketHeader();
|
||||||
|
|
||||||
|
header.isAuthenticated = isAuthed ? (byte)1 : (byte)0;
|
||||||
|
header.isEncrypted = isEncrypted ? (byte)1 : (byte)0;
|
||||||
|
header.numSubpackets = (ushort)1;
|
||||||
|
header.packetSize = BASEPACKET_SIZE;
|
||||||
|
header.timestamp = Utils.MilisUnixTimeStampUTC();
|
||||||
|
|
||||||
|
//Get packet size
|
||||||
|
header.packetSize += (ushort)data.Length;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
58
FFXIVClassic_Lobby_Server/packets/HardCoded_Packets.cs
Normal file
58
FFXIVClassic_Lobby_Server/packets/HardCoded_Packets.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
162
FFXIVClassic_Lobby_Server/packets/SubPacket.cs
Normal file
162
FFXIVClassic_Lobby_Server/packets/SubPacket.cs
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
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 type;
|
||||||
|
public uint sourceId;
|
||||||
|
public uint targetId;
|
||||||
|
public uint unknown1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct GameMessageHeader
|
||||||
|
{
|
||||||
|
public ushort unknown4; //Always 0x14
|
||||||
|
public ushort opcode;
|
||||||
|
public uint unknown5;
|
||||||
|
public uint timestamp;
|
||||||
|
public uint unknown6;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SubPacket
|
||||||
|
{
|
||||||
|
public const int SUBPACKET_SIZE = 0x10;
|
||||||
|
public const int GAMEMESSAGE_SIZE = 0x10;
|
||||||
|
|
||||||
|
public SubPacketHeader header;
|
||||||
|
public GameMessageHeader gameMessage;
|
||||||
|
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 (header.type == 0x3)
|
||||||
|
{
|
||||||
|
fixed (byte* pdata = &bytes[offset + SUBPACKET_SIZE])
|
||||||
|
{
|
||||||
|
gameMessage = (GameMessageHeader)Marshal.PtrToStructure(new IntPtr(pdata), typeof(GameMessageHeader));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes.Length < offset + header.subpacketSize)
|
||||||
|
throw new OverflowException("Packet Error: Subpacket size didn't equal subpacket data");
|
||||||
|
|
||||||
|
if (header.type == 0x3)
|
||||||
|
{
|
||||||
|
data = new byte[header.subpacketSize - SUBPACKET_SIZE - GAMEMESSAGE_SIZE];
|
||||||
|
Array.Copy(bytes, offset + SUBPACKET_SIZE + GAMEMESSAGE_SIZE, data, 0, data.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = new byte[header.subpacketSize - SUBPACKET_SIZE];
|
||||||
|
Array.Copy(bytes, offset + SUBPACKET_SIZE, data, 0, data.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += header.subpacketSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubPacket(ushort opcode, uint sourceId, uint targetId, byte[] data)
|
||||||
|
{
|
||||||
|
this.header = new SubPacketHeader();
|
||||||
|
this.gameMessage = new GameMessageHeader();
|
||||||
|
|
||||||
|
gameMessage.opcode = opcode;
|
||||||
|
header.sourceId = sourceId;
|
||||||
|
header.targetId = targetId;
|
||||||
|
|
||||||
|
gameMessage.timestamp = Utils.UnixTimeStampUTC();
|
||||||
|
|
||||||
|
header.type = 0x03;
|
||||||
|
header.unknown1 = 0x00;
|
||||||
|
gameMessage.unknown4 = 0x14;
|
||||||
|
gameMessage.unknown5 = 0x00;
|
||||||
|
gameMessage.unknown6 = 0x00;
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
|
||||||
|
header.subpacketSize = (ushort)(SUBPACKET_SIZE + GAMEMESSAGE_SIZE + data.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubPacket(SubPacket original, uint newTargetId)
|
||||||
|
{
|
||||||
|
this.header = new SubPacketHeader();
|
||||||
|
this.gameMessage = original.gameMessage;
|
||||||
|
header.subpacketSize = original.header.subpacketSize;
|
||||||
|
header.type = original.header.type;
|
||||||
|
header.sourceId = original.header.sourceId;
|
||||||
|
header.targetId = newTargetId;
|
||||||
|
data = original.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
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[] getGameMessageBytes()
|
||||||
|
{
|
||||||
|
int size = Marshal.SizeOf(gameMessage);
|
||||||
|
byte[] arr = new byte[size];
|
||||||
|
|
||||||
|
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||||
|
Marshal.StructureToPtr(gameMessage, 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);
|
||||||
|
|
||||||
|
if (header.type == 0x3)
|
||||||
|
Array.Copy(getGameMessageBytes(), 0, outBytes, SUBPACKET_SIZE, GAMEMESSAGE_SIZE);
|
||||||
|
|
||||||
|
Array.Copy(data, 0, outBytes, SUBPACKET_SIZE + (header.type == 0x3 ? GAMEMESSAGE_SIZE : 0), data.Length);
|
||||||
|
return outBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void debugPrintSubPacket()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Console.BackgroundColor = ConsoleColor.DarkRed;
|
||||||
|
Console.WriteLine("Size: 0x{0:X}", header.subpacketSize);
|
||||||
|
if (header.type == 0x03)
|
||||||
|
Console.WriteLine("Opcode: 0x{0:X}", gameMessage.opcode);
|
||||||
|
Console.WriteLine("{0}", Utils.ByteArrayToHex(getHeaderBytes()));
|
||||||
|
if (header.type == 0x03)
|
||||||
|
Console.WriteLine("{0}", Utils.ByteArrayToHex(getGameMessageBytes()));
|
||||||
|
Console.BackgroundColor = ConsoleColor.DarkMagenta;
|
||||||
|
Console.WriteLine("{0}", Utils.ByteArrayToHex(data));
|
||||||
|
Console.BackgroundColor = ConsoleColor.Black;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets.receive
|
||||||
|
{
|
||||||
|
class CharacterModifyPacket
|
||||||
|
{
|
||||||
|
public UInt64 sequence;
|
||||||
|
public uint characterId;
|
||||||
|
public uint personType;
|
||||||
|
public byte slot;
|
||||||
|
public byte command;
|
||||||
|
public ushort worldId;
|
||||||
|
public String characterName;
|
||||||
|
public String characterInfoEncoded;
|
||||||
|
|
||||||
|
public bool invalidPacket = false;
|
||||||
|
|
||||||
|
public CharacterModifyPacket(byte[] data)
|
||||||
|
{
|
||||||
|
using (MemoryStream mem = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
using (BinaryReader binReader = new BinaryReader(mem))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sequence = binReader.ReadUInt64();
|
||||||
|
characterId = binReader.ReadUInt32();
|
||||||
|
personType = binReader.ReadUInt32();
|
||||||
|
slot = binReader.ReadByte();
|
||||||
|
command = binReader.ReadByte();
|
||||||
|
worldId = binReader.ReadUInt16();
|
||||||
|
|
||||||
|
characterName = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' });
|
||||||
|
characterInfoEncoded = Encoding.ASCII.GetString(binReader.ReadBytes(0x190)).Trim(new[] { '\0' });
|
||||||
|
}
|
||||||
|
catch (Exception){
|
||||||
|
invalidPacket = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets.receive
|
||||||
|
{
|
||||||
|
class SecurityHandshakePacket
|
||||||
|
{
|
||||||
|
public string ticketPhrase;
|
||||||
|
public uint clientNumber;
|
||||||
|
|
||||||
|
public bool invalidPacket = false;
|
||||||
|
|
||||||
|
public SecurityHandshakePacket(byte[] data)
|
||||||
|
{
|
||||||
|
using (MemoryStream mem = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
using (BinaryReader binReader = new BinaryReader(mem))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
binReader.BaseStream.Seek(0x34, SeekOrigin.Begin);
|
||||||
|
ticketPhrase = Encoding.ASCII.GetString(binReader.ReadBytes(0x40)).Trim(new[] { '\0' });
|
||||||
|
clientNumber = binReader.ReadUInt32();
|
||||||
|
}
|
||||||
|
catch (Exception){
|
||||||
|
invalidPacket = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets.receive
|
||||||
|
{
|
||||||
|
class SelectCharacterPacket
|
||||||
|
{
|
||||||
|
public UInt64 sequence;
|
||||||
|
public uint characterId;
|
||||||
|
public uint unknownId;
|
||||||
|
public UInt64 ticket;
|
||||||
|
|
||||||
|
public bool invalidPacket = false;
|
||||||
|
|
||||||
|
public SelectCharacterPacket(byte[] data)
|
||||||
|
{
|
||||||
|
using (MemoryStream mem = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
using (BinaryReader binReader = new BinaryReader(mem))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sequence = binReader.ReadUInt64();
|
||||||
|
characterId = binReader.ReadUInt32();
|
||||||
|
unknownId = binReader.ReadUInt32();
|
||||||
|
ticket = binReader.ReadUInt64();
|
||||||
|
}
|
||||||
|
catch (Exception){
|
||||||
|
invalidPacket = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
38
FFXIVClassic_Lobby_Server/packets/receive/SessionPacket.cs
Normal file
38
FFXIVClassic_Lobby_Server/packets/receive/SessionPacket.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets.receive
|
||||||
|
{
|
||||||
|
class SessionPacket
|
||||||
|
{
|
||||||
|
public bool invalidPacket = false;
|
||||||
|
public UInt64 sequence;
|
||||||
|
public String session;
|
||||||
|
public String version;
|
||||||
|
|
||||||
|
public SessionPacket(byte[] data)
|
||||||
|
{
|
||||||
|
using (MemoryStream mem = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
using (BinaryReader binReader = new BinaryReader(mem))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
sequence = binReader.ReadUInt64();
|
||||||
|
binReader.ReadUInt32();
|
||||||
|
binReader.ReadUInt32();
|
||||||
|
session = Encoding.ASCII.GetString(binReader.ReadBytes(0x40)).Trim(new[] { '\0' });
|
||||||
|
version = Encoding.ASCII.GetString(binReader.ReadBytes(0x20)).Trim(new[] { '\0' });
|
||||||
|
}
|
||||||
|
catch (Exception){
|
||||||
|
invalidPacket = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
FFXIVClassic_Lobby_Server/packets/send/AccountListPacket.cs
Normal file
99
FFXIVClassic_Lobby_Server/packets/send/AccountListPacket.cs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
using FFXIVClassic_Lobby_Server.dataobjects;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets
|
||||||
|
{
|
||||||
|
class AccountListPacket
|
||||||
|
{
|
||||||
|
public const ushort OPCODE = 0x0C;
|
||||||
|
public const ushort MAXPERPACKET = 8;
|
||||||
|
|
||||||
|
private UInt64 sequence;
|
||||||
|
private List<Account> accountList;
|
||||||
|
|
||||||
|
public AccountListPacket(UInt64 sequence, List<Account> accountList)
|
||||||
|
{
|
||||||
|
this.sequence = sequence;
|
||||||
|
this.accountList = accountList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SubPacket> buildPackets()
|
||||||
|
{
|
||||||
|
List<SubPacket> subPackets = new List<SubPacket>();
|
||||||
|
|
||||||
|
int accountCount = 0;
|
||||||
|
int totalCount = 0;
|
||||||
|
|
||||||
|
MemoryStream memStream = null;
|
||||||
|
BinaryWriter binWriter = null;
|
||||||
|
|
||||||
|
foreach (Account account in accountList)
|
||||||
|
{
|
||||||
|
if (totalCount == 0 || accountCount % MAXPERPACKET == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x280);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (UInt32)(accountList.Count - totalCount) : (UInt32)MAXPERPACKET);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write Entries
|
||||||
|
binWriter.Write((UInt32)account.id);
|
||||||
|
binWriter.Write((UInt32)0);
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(account.name.PadRight(0x40, '\0')));
|
||||||
|
|
||||||
|
accountCount++;
|
||||||
|
totalCount++;
|
||||||
|
|
||||||
|
//Send this chunk of world list
|
||||||
|
if (accountCount >= MAXPERPACKET)
|
||||||
|
{
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
accountCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//If there is anything left that was missed or the list is empty
|
||||||
|
if (accountCount > 0 || accountList.Count == 0)
|
||||||
|
{
|
||||||
|
if (accountList.Count == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x210);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write Empty List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(accountList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write((UInt32)0);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subPackets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
67
FFXIVClassic_Lobby_Server/packets/send/CharaCreatorPacket.cs
Normal file
67
FFXIVClassic_Lobby_Server/packets/send/CharaCreatorPacket.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets
|
||||||
|
{
|
||||||
|
class CharaCreatorPacket
|
||||||
|
{
|
||||||
|
public const ushort OPCODE = 0x0E;
|
||||||
|
|
||||||
|
public const ushort RESERVE = 0x01;
|
||||||
|
public const ushort MAKE = 0x02;
|
||||||
|
public const ushort RENAME = 0x03;
|
||||||
|
public const ushort DELETE = 0x04;
|
||||||
|
public const ushort RENAME_RETAINER = 0x06;
|
||||||
|
|
||||||
|
private UInt64 sequence;
|
||||||
|
|
||||||
|
private ushort command;
|
||||||
|
private uint pid;
|
||||||
|
private uint cid;
|
||||||
|
private uint type;
|
||||||
|
private uint ticket;
|
||||||
|
private string charaName;
|
||||||
|
private string worldName;
|
||||||
|
|
||||||
|
public CharaCreatorPacket(UInt64 sequence, ushort command, uint pid, uint cid, uint ticket, string charaName, string worldName)
|
||||||
|
{
|
||||||
|
this.sequence = sequence;
|
||||||
|
this.command = command;
|
||||||
|
this.pid = pid;
|
||||||
|
this.cid = cid;
|
||||||
|
this.type = 0x400017;
|
||||||
|
this.ticket = ticket;
|
||||||
|
this.charaName = charaName;
|
||||||
|
this.worldName = worldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubPacket buildPacket()
|
||||||
|
{
|
||||||
|
MemoryStream memStream = new MemoryStream(0x1F0);
|
||||||
|
BinaryWriter binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
binWriter.Write((byte)1);
|
||||||
|
binWriter.Write((byte)1);
|
||||||
|
binWriter.Write((UInt16)command);
|
||||||
|
binWriter.Write((UInt32)0);
|
||||||
|
|
||||||
|
binWriter.Write((UInt32)pid); //PID
|
||||||
|
binWriter.Write((UInt32)cid); //CID
|
||||||
|
binWriter.Write((UInt32)type); //Type?
|
||||||
|
binWriter.Write((UInt32)ticket); //Ticket
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(charaName.PadRight(0x20, '\0'))); //Name
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(worldName.PadRight(0x20, '\0'))); //World Name
|
||||||
|
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
|
||||||
|
return new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
157
FFXIVClassic_Lobby_Server/packets/send/CharacterListPacket.cs
Normal file
157
FFXIVClassic_Lobby_Server/packets/send/CharacterListPacket.cs
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
using FFXIVClassic_Lobby_Server.dataobjects;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets
|
||||||
|
{
|
||||||
|
class CharacterListPacket
|
||||||
|
{
|
||||||
|
public const ushort OPCODE = 0x0D;
|
||||||
|
public const ushort MAXPERPACKET = 2;
|
||||||
|
|
||||||
|
private ulong sequence;
|
||||||
|
private List<Character> characterList;
|
||||||
|
|
||||||
|
public CharacterListPacket(ulong sequence, List<Character> characterList)
|
||||||
|
{
|
||||||
|
this.sequence = sequence;
|
||||||
|
this.characterList = characterList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SubPacket> buildPackets()
|
||||||
|
{
|
||||||
|
List<SubPacket> subPackets = new List<SubPacket>();
|
||||||
|
|
||||||
|
int numCharacters = characterList.Count >= 8 ? 8 : characterList.Count + 1;
|
||||||
|
|
||||||
|
int characterCount = 0;
|
||||||
|
int totalCount = 0;
|
||||||
|
|
||||||
|
MemoryStream memStream = null;
|
||||||
|
BinaryWriter binWriter = null;
|
||||||
|
|
||||||
|
foreach (Character chara in characterList)
|
||||||
|
{
|
||||||
|
Appearance appearance = Database.getAppearance(chara.id);
|
||||||
|
|
||||||
|
if (totalCount == 0 || characterCount % MAXPERPACKET == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x3B0);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (UInt32)(numCharacters - totalCount) : (UInt32)MAXPERPACKET);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
binWriter.Seek(0x10 + (0x1D0 * characterCount), SeekOrigin.Begin);
|
||||||
|
|
||||||
|
//Write Entries
|
||||||
|
World world = Database.getServer(chara.serverId);
|
||||||
|
string worldname = world == null ? "Unknown" : world.name;
|
||||||
|
|
||||||
|
binWriter.Write((uint)0); //???
|
||||||
|
binWriter.Write((uint)chara.id); //Character Id
|
||||||
|
binWriter.Write((byte)(totalCount)); //Slot
|
||||||
|
|
||||||
|
byte options = 0;
|
||||||
|
if (chara.state == 1)
|
||||||
|
options |= 0x01;
|
||||||
|
if (chara.doRename)
|
||||||
|
options |= 0x02;
|
||||||
|
if (chara.isLegacy)
|
||||||
|
options |= 0x08;
|
||||||
|
|
||||||
|
binWriter.Write((byte)options); //Options (0x01: Service Account not active, 0x72: Change Chara Name)
|
||||||
|
binWriter.Write((ushort)0);
|
||||||
|
binWriter.Write((uint)chara.currentZoneId); //Logged out zone
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(chara.name.PadRight(0x20, '\0'))); //Name
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(worldname.PadRight(0xE, '\0'))); //World Name
|
||||||
|
|
||||||
|
binWriter.Write(CharaInfo.buildForCharaList(chara, appearance)); //Appearance Data
|
||||||
|
//binWriter.Write(CharaInfo.debug()); //Appearance Data
|
||||||
|
|
||||||
|
characterCount++;
|
||||||
|
totalCount++;
|
||||||
|
|
||||||
|
//Send this chunk of character list
|
||||||
|
if (characterCount >= MAXPERPACKET)
|
||||||
|
{
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
characterCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Incase DB came back with more than max
|
||||||
|
if (totalCount >= 8)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add a 'NEW' slot if there is space
|
||||||
|
if (characterList.Count < 8)
|
||||||
|
{
|
||||||
|
if (characterCount % MAXPERPACKET == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x3B0);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write(numCharacters - totalCount <= MAXPERPACKET ? (UInt32)(numCharacters-totalCount) : (UInt32)MAXPERPACKET);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
binWriter.Seek(0x10 + (0x1D0 * characterCount), SeekOrigin.Begin);
|
||||||
|
|
||||||
|
//Write Entries
|
||||||
|
binWriter.Write((uint)0); //???
|
||||||
|
binWriter.Write((uint)0); //Character Id
|
||||||
|
binWriter.Write((byte)(totalCount)); //Slot
|
||||||
|
binWriter.Write((byte)0); //Options (0x01: Service Account not active, 0x72: Change Chara Name)
|
||||||
|
binWriter.Write((ushort)0);
|
||||||
|
binWriter.Write((uint)0); //Logged out zone
|
||||||
|
|
||||||
|
characterCount++;
|
||||||
|
totalCount++;
|
||||||
|
|
||||||
|
//Send this chunk of character list
|
||||||
|
if (characterCount >= MAXPERPACKET)
|
||||||
|
{
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
characterCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If there is anything left that was missed or the list is empty
|
||||||
|
if (characterCount > 0 || numCharacters == 0)
|
||||||
|
{
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subPackets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
FFXIVClassic_Lobby_Server/packets/send/ErrorPacket.cs
Normal file
47
FFXIVClassic_Lobby_Server/packets/send/ErrorPacket.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets
|
||||||
|
{
|
||||||
|
class ErrorPacket
|
||||||
|
{
|
||||||
|
private const ushort OPCODE = 0x02;
|
||||||
|
|
||||||
|
private UInt64 sequence;
|
||||||
|
private uint errorCode;
|
||||||
|
private uint statusCode;
|
||||||
|
private uint textId;
|
||||||
|
private string message;
|
||||||
|
|
||||||
|
public ErrorPacket(UInt64 sequence, uint errorCode, uint statusCode, uint textId, string message)
|
||||||
|
{
|
||||||
|
this.sequence = sequence;
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
this.statusCode = statusCode;
|
||||||
|
this.textId = textId;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubPacket buildPacket()
|
||||||
|
{
|
||||||
|
MemoryStream memStream = new MemoryStream(0x210);
|
||||||
|
BinaryWriter binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
binWriter.Write(sequence);
|
||||||
|
binWriter.Write(errorCode);
|
||||||
|
binWriter.Write(statusCode);
|
||||||
|
binWriter.Write(textId);
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(message));
|
||||||
|
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
return subpacket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
103
FFXIVClassic_Lobby_Server/packets/send/ImportListPacket.cs
Normal file
103
FFXIVClassic_Lobby_Server/packets/send/ImportListPacket.cs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
using FFXIVClassic_Lobby_Server.dataobjects;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets
|
||||||
|
{
|
||||||
|
class ImportListPacket
|
||||||
|
{
|
||||||
|
public const ushort OPCODE = 0x16;
|
||||||
|
public const ushort MAXPERPACKET = 12;
|
||||||
|
|
||||||
|
private UInt64 sequence;
|
||||||
|
private List<String> namesList;
|
||||||
|
|
||||||
|
public ImportListPacket(UInt64 sequence, List<String> names)
|
||||||
|
{
|
||||||
|
this.sequence = sequence;
|
||||||
|
this.namesList = names;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SubPacket> buildPackets()
|
||||||
|
{
|
||||||
|
List<SubPacket> subPackets = new List<SubPacket>();
|
||||||
|
|
||||||
|
int namesCount = 0;
|
||||||
|
int totalCount = 0;
|
||||||
|
|
||||||
|
MemoryStream memStream = null;
|
||||||
|
BinaryWriter binWriter = null;
|
||||||
|
|
||||||
|
foreach (String name in namesList)
|
||||||
|
{
|
||||||
|
if (totalCount == 0 || namesCount % MAXPERPACKET == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x210);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (UInt32)(namesList.Count - totalCount) : (UInt32)MAXPERPACKET);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write Entries
|
||||||
|
binWriter.Write((uint)0);
|
||||||
|
binWriter.Write((uint)totalCount);
|
||||||
|
|
||||||
|
if (!name.Contains(" "))
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes((name+" Last").PadRight(0x20, '\0')));
|
||||||
|
else
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(name.PadRight(0x20, '\0')));
|
||||||
|
|
||||||
|
namesCount++;
|
||||||
|
totalCount++;
|
||||||
|
|
||||||
|
//Send this chunk of world list
|
||||||
|
if (namesCount >= MAXPERPACKET)
|
||||||
|
{
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
namesCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//If there is anything left that was missed or the list is empty
|
||||||
|
if (namesCount > 0 || namesList.Count == 0)
|
||||||
|
{
|
||||||
|
if (namesList.Count == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x210);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write Empty List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(namesList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write((UInt32)0);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subPackets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
104
FFXIVClassic_Lobby_Server/packets/send/RetainerListPacket.cs
Normal file
104
FFXIVClassic_Lobby_Server/packets/send/RetainerListPacket.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
using FFXIVClassic_Lobby_Server.dataobjects;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets
|
||||||
|
{
|
||||||
|
class RetainerListPacket
|
||||||
|
{
|
||||||
|
public const ushort OPCODE = 0x17;
|
||||||
|
public const ushort MAXPERPACKET = 9;
|
||||||
|
|
||||||
|
private UInt64 sequence;
|
||||||
|
private List<Retainer> retainerList;
|
||||||
|
|
||||||
|
public RetainerListPacket(UInt64 sequence, List<Retainer> retainerList)
|
||||||
|
{
|
||||||
|
this.sequence = sequence;
|
||||||
|
this.retainerList = retainerList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SubPacket> buildPackets()
|
||||||
|
{
|
||||||
|
List<SubPacket> subPackets = new List<SubPacket>();
|
||||||
|
|
||||||
|
int retainerCount = 0;
|
||||||
|
int totalCount = 0;
|
||||||
|
|
||||||
|
MemoryStream memStream = null;
|
||||||
|
BinaryWriter binWriter = null;
|
||||||
|
|
||||||
|
foreach (Retainer retainer in retainerList)
|
||||||
|
{
|
||||||
|
if (totalCount == 0 || retainerCount % MAXPERPACKET == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x210);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (UInt32)(retainerList.Count - totalCount) : (UInt32)MAXPERPACKET);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
binWriter.Write((UInt64)0);
|
||||||
|
binWriter.Write((UInt32)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write Entries
|
||||||
|
binWriter.Write((uint)retainer.id);
|
||||||
|
binWriter.Write((uint)retainer.characterId);
|
||||||
|
binWriter.Write((ushort)retainer.slot);
|
||||||
|
binWriter.Write((ushort)(retainer.doRename ? 0x04 : 0x00));
|
||||||
|
binWriter.Write((uint)0);
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(retainer.name.PadRight(0x20, '\0')));
|
||||||
|
|
||||||
|
retainerCount++;
|
||||||
|
totalCount++;
|
||||||
|
|
||||||
|
//Send this chunk of character list
|
||||||
|
if (retainerCount >= MAXPERPACKET)
|
||||||
|
{
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
retainerCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//If there is anything left that was missed or the list is empty
|
||||||
|
if (retainerCount > 0 || retainerList.Count == 0)
|
||||||
|
{
|
||||||
|
if (retainerList.Count == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x210);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write Empty List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(retainerList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write((UInt32)0);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subPackets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets
|
||||||
|
{
|
||||||
|
class SelectCharacterConfirmPacket
|
||||||
|
{
|
||||||
|
public const ushort OPCODE = 0x0F;
|
||||||
|
|
||||||
|
private UInt64 sequence;
|
||||||
|
private UInt32 characterId;
|
||||||
|
private string sessionToken;
|
||||||
|
private string worldIp;
|
||||||
|
private UInt16 worldPort;
|
||||||
|
private UInt64 selectCharTicket;
|
||||||
|
|
||||||
|
public SelectCharacterConfirmPacket(UInt64 sequence, UInt32 characterId, string sessionToken, string worldIp, ushort worldPort, UInt64 selectCharTicket)
|
||||||
|
{
|
||||||
|
this.sequence = sequence;
|
||||||
|
this.characterId = characterId;
|
||||||
|
this.sessionToken = sessionToken;
|
||||||
|
this.worldIp = worldIp;
|
||||||
|
this.worldPort = worldPort;
|
||||||
|
this.selectCharTicket = selectCharTicket;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SubPacket> buildPackets()
|
||||||
|
{
|
||||||
|
List<SubPacket> subPackets = new List<SubPacket>();
|
||||||
|
|
||||||
|
byte[] data;
|
||||||
|
|
||||||
|
using (MemoryStream memStream = new MemoryStream(0x98))
|
||||||
|
{
|
||||||
|
using (BinaryWriter binWriter = new BinaryWriter(memStream))
|
||||||
|
{
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
binWriter.Write((UInt32)characterId); //ActorId
|
||||||
|
binWriter.Write((UInt32)characterId); //CharacterId
|
||||||
|
binWriter.Write((UInt32)0);
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(sessionToken.PadRight(0x42, '\0'))); //Session Token
|
||||||
|
binWriter.Write((UInt16)worldPort); //World Port
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(worldIp.PadRight(0x38, '\0'))); //World Hostname/IP
|
||||||
|
binWriter.Write((UInt64)selectCharTicket); //Ticket or Handshake of somekind
|
||||||
|
}
|
||||||
|
data = memStream.GetBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
|
||||||
|
return subPackets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
101
FFXIVClassic_Lobby_Server/packets/send/WorldListPacket.cs
Normal file
101
FFXIVClassic_Lobby_Server/packets/send/WorldListPacket.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
using FFXIVClassic_Lobby_Server.dataobjects;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.packets
|
||||||
|
{
|
||||||
|
class WorldListPacket
|
||||||
|
{
|
||||||
|
public const ushort OPCODE = 0x15;
|
||||||
|
public const ushort MAXPERPACKET = 6;
|
||||||
|
|
||||||
|
private UInt64 sequence;
|
||||||
|
private List<World> worldList;
|
||||||
|
|
||||||
|
public WorldListPacket(UInt64 sequence, List<World> serverList)
|
||||||
|
{
|
||||||
|
this.sequence = sequence;
|
||||||
|
this.worldList = serverList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SubPacket> buildPackets()
|
||||||
|
{
|
||||||
|
List<SubPacket> subPackets = new List<SubPacket>();
|
||||||
|
|
||||||
|
int serverCount = 0;
|
||||||
|
int totalCount = 0;
|
||||||
|
|
||||||
|
MemoryStream memStream = null;
|
||||||
|
BinaryWriter binWriter = null;
|
||||||
|
|
||||||
|
foreach (World world in worldList)
|
||||||
|
{
|
||||||
|
if (totalCount == 0 || serverCount % MAXPERPACKET == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x210);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (UInt32)(worldList.Count - totalCount) : (UInt32)MAXPERPACKET);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write Entries
|
||||||
|
binWriter.Write((ushort)world.id);
|
||||||
|
binWriter.Write((ushort)world.listPosition);
|
||||||
|
binWriter.Write((uint)world.population);
|
||||||
|
binWriter.Write((UInt64)0);
|
||||||
|
binWriter.Write(Encoding.ASCII.GetBytes(world.name.PadRight(64, '\0')));
|
||||||
|
|
||||||
|
serverCount++;
|
||||||
|
totalCount++;
|
||||||
|
|
||||||
|
//Send this chunk of world list
|
||||||
|
if (serverCount >= MAXPERPACKET)
|
||||||
|
{
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
serverCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//If there is anything left that was missed or the list is empty
|
||||||
|
if (serverCount > 0 || worldList.Count == 0)
|
||||||
|
{
|
||||||
|
if (worldList.Count == 0)
|
||||||
|
{
|
||||||
|
memStream = new MemoryStream(0x210);
|
||||||
|
binWriter = new BinaryWriter(memStream);
|
||||||
|
|
||||||
|
//Write Empty List Info
|
||||||
|
binWriter.Write((UInt64)sequence);
|
||||||
|
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
|
||||||
|
binWriter.Write(worldList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
|
||||||
|
binWriter.Write((UInt32)0);
|
||||||
|
binWriter.Write((byte)0);
|
||||||
|
binWriter.Write((UInt16)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data = memStream.GetBuffer();
|
||||||
|
binWriter.Dispose();
|
||||||
|
memStream.Dispose();
|
||||||
|
SubPacket subpacket = new SubPacket(OPCODE, 0xe0006868, 0xe0006868, data);
|
||||||
|
subPackets.Add(subpacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subPackets;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
FFXIVClassic_Lobby_Server/utils/CharacterCreatorUtils.cs
Normal file
53
FFXIVClassic_Lobby_Server/utils/CharacterCreatorUtils.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FFXIVClassic_Lobby_Server.utils
|
||||||
|
{
|
||||||
|
class CharacterCreatorUtils
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<uint, uint[]> equipmentAppearance = new Dictionary<uint, uint[]>
|
||||||
|
{
|
||||||
|
{ 2, new uint[]{60818432,0,0,0,0,0,0,0,10656,10560,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //PUG
|
||||||
|
{ 3, new uint[]{79692890,0,0,0,0,0,0,0,31776,4448,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //GLA
|
||||||
|
{ 4, new uint[]{147850310,0,0,0,0,0,0,23713,1184,10016,5472,1152,6144,0,0,0,0,0,0,0,0,0} }, //MRD
|
||||||
|
{ 7, new uint[]{210764860,236979210,0,0,0,231736320,0,0,9888,9984,1024,25824,6144,0,0,0,0,0,0,0,0,0} }, //ARC
|
||||||
|
{ 8, new uint[]{168823858,0,0,0,0,0,0,0,13920,7200,1024,10656,6144,0,0,0,0,0,0,0,0,0} }, //LNC
|
||||||
|
|
||||||
|
{ 22, new uint[]{294650980,0,0,0,0,0,0,0,7744,5472,1024,5504,4096,0,0,0,0,0,0,0,0,0} }, //THM
|
||||||
|
{ 23, new uint[]{347079700,0,0,0,0,0,0,0,4448,2240,1024,4416,4096,0,0,0,0,0,0,0,0,0} }, //CNJ
|
||||||
|
|
||||||
|
{ 29, new uint[]{705692672,0,0,0,0,0,0,0,1184,10016,10656,9632,2048,0,0,0,0,0,0,0,0,0} }, //CRP
|
||||||
|
{ 30, new uint[]{721421372,0,0,0,0,0,0,0,1184,2241,2336,2304,2048,0,0,0,0,0,0,0,0,0} }, //BSM
|
||||||
|
{ 31, new uint[]{737149962,0,0,0,0,0,0,0,32992,2240,1024,2272,2048,0,0,0,0,0,0,0,0,0} }, //ARM
|
||||||
|
{ 32, new uint[]{752878592,0,0,0,0,0,0,0,2368,3424,1024,10656,2048,0,0,0,0,0,0,0,0,0} }, //GSM
|
||||||
|
{ 33, new uint[]{768607252,0,0,0,0,0,0,4448,4449,1792,1024,21888,2048,0,0,0,0,0,0,0,0,0} }, //LTW
|
||||||
|
{ 34, new uint[]{784335922,0,0,0,0,0,0,0,5505,5473,1024,5505,2048,0,0,0,0,0,0,0,0,0} }, //WVR
|
||||||
|
{ 35, new uint[]{800064522,0,0,0,0,0,0,20509,5504,2241,1024,1152,2048,0,0,0,0,0,0,0,0,0} }, //ALC
|
||||||
|
{ 36, new uint[]{815793192,0,0,0,0,0,0,5632,34848,1792,1024,25825,2048,0,0,0,0,0,0,0,0,0} }, //CUL
|
||||||
|
|
||||||
|
{ 39, new uint[]{862979092,0,0,0,0,0,0,0,1184,2242,6464,6528,14336,0,0,0,0,0,0,0,0,0} }, //MIN
|
||||||
|
{ 40, new uint[]{878707732,0,0,0,0,0,0,6304,6624,6560,1024,1152,14336,0,0,0,0,0,0,0,0,0} }, //BOT
|
||||||
|
{ 41, new uint[]{894436372,0,0,0,0,0,0,6400,1184,9984,1024,6529,14336,0,0,0,0,0,0,0,0,0} }, //FSH
|
||||||
|
};
|
||||||
|
|
||||||
|
public static uint[] getEquipmentForClass(uint charClass)
|
||||||
|
{
|
||||||
|
if (equipmentAppearance.ContainsKey(charClass))
|
||||||
|
return equipmentAppearance[charClass];
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository path="..\FFXIVClassic Map Server\packages.config" />
|
<repository path="..\FFXIVClassic Map Server\packages.config" />
|
||||||
|
<repository path="..\FFXIVClassic_Lobby_Server\packages.config" />
|
||||||
</repositories>
|
</repositories>
|
63
research/encodedCharaInfo.txt
Normal file
63
research/encodedCharaInfo.txt
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
===Encoded Chara Info=== By Ioncannon
|
||||||
|
-Based on chara info array in Seventh Umbral
|
||||||
|
|
||||||
|
0x000: Int32;
|
||||||
|
0x004: Int32;
|
||||||
|
0x008:Name Size Int32;
|
||||||
|
0x00C:Name String; Variable size, but in file the name "Wrenix Wrong" is 0xD in size
|
||||||
|
0x019:Size? Offset? Int32; Must be 0x1c or is crashes....
|
||||||
|
0x01D:Unknown Int32;
|
||||||
|
0x021:Tribe Model Int32;
|
||||||
|
0x025:Size Int32;
|
||||||
|
0x029:Colors Info Int32;
|
||||||
|
0x02D:Face Info Int32;
|
||||||
|
0x031:Hair Style + Highlight Color Int32;
|
||||||
|
0x035:Voice Int32;
|
||||||
|
0x039:MainHand Int32;
|
||||||
|
0x03D:OffHand Int32;
|
||||||
|
0x041: Int32;
|
||||||
|
0x045: Int32;
|
||||||
|
0x049: Int32;
|
||||||
|
0x04D: Int32;
|
||||||
|
0x051: Int32;
|
||||||
|
0x055:Head Int32;
|
||||||
|
0x059:Body Int32;
|
||||||
|
0x06D:Legs Int32;
|
||||||
|
0x061:Hands Int32;
|
||||||
|
0x065:Feet Int32;
|
||||||
|
0x069:Waist Int32;
|
||||||
|
0x06D: Int32;
|
||||||
|
0x071:Right Ear Int32;
|
||||||
|
0x075:Left Ear Int32;
|
||||||
|
0x079: Int32;
|
||||||
|
0x07D: Int32;
|
||||||
|
0x081:Right Ring Int32;
|
||||||
|
0x085:Left Ring Int32;
|
||||||
|
|
||||||
|
====Zeros/Unknown====
|
||||||
|
|
||||||
|
0x091:ID????? Int32;
|
||||||
|
0x095:Unknown (Must be > 0x00) Int32;
|
||||||
|
0x099:Class Byte;
|
||||||
|
0x09A:Level Short;
|
||||||
|
0x09C:Job Byte;
|
||||||
|
0x09D:Unknown Short;
|
||||||
|
0x09F:Tribe Byte;
|
||||||
|
|
||||||
|
0x0A0: Int32;
|
||||||
|
0x0A4:Location Str Size Int32;
|
||||||
|
0x0A8:Location String String; Variable size, but in file it is prv0Inn01\0, 0x0A in size.
|
||||||
|
|
||||||
|
0x0B2:Territory Str Size Int32;
|
||||||
|
0x0B6:Territory Str? String; Variable size, but in file it is defaultTerritory\0, 0x11 in size.
|
||||||
|
|
||||||
|
0x0C7:Guardian Byte;
|
||||||
|
0x0C8:Birth Month Byte;
|
||||||
|
0x0C9:Birth Day Byte;
|
||||||
|
|
||||||
|
0x0CA: Short;
|
||||||
|
0x0CC: Int32;
|
||||||
|
0x0D0: Int32;
|
||||||
|
|
||||||
|
0x0E4: Byte;
|
||||||
|
0x0E8:Allegiance Byte;
|
38
research/encodedCharaMakeInfo.txt
Normal file
38
research/encodedCharaMakeInfo.txt
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
===Encoded CharaMake Info=== By Ioncannon
|
||||||
|
-Based on chara info array in Seventh Umbral
|
||||||
|
|
||||||
|
0x00: Unknown... Version? Int32;
|
||||||
|
0x04: Unknown - Weird #1 Int32;
|
||||||
|
0x08: Tribe
|
||||||
|
0x09: Size
|
||||||
|
0x0A: Hair Style Short
|
||||||
|
0x0C: Highlight Hair Color Short;
|
||||||
|
0x0E: Face
|
||||||
|
0x0F: Characteristics
|
||||||
|
0x10: Characteristics Color Short;
|
||||||
|
0x12: Unknown - Weird #2 Int32;
|
||||||
|
0x15: Eyebrows
|
||||||
|
0x16: Eye Size
|
||||||
|
0x17: Eye Shape
|
||||||
|
0x18: Nose
|
||||||
|
0x19: Feature
|
||||||
|
0x1A: Mouth
|
||||||
|
0x1B: Ears
|
||||||
|
0x1C: Hair Color Short;
|
||||||
|
0x1E: Unknown - Weird #3 Int32;
|
||||||
|
0x22: Skin Color Short
|
||||||
|
0x24: Eye Color Short;
|
||||||
|
0x26: Voice
|
||||||
|
0x27: Guardian
|
||||||
|
0x28: Month
|
||||||
|
0x29: Day
|
||||||
|
0x2A: Start Class Short;
|
||||||
|
0x2C: Unknown Int32;
|
||||||
|
0x30: Unknown Int32;
|
||||||
|
0x34: Unknown Int32;
|
||||||
|
|
||||||
|
0x38: 0x10 bytes of 0s
|
||||||
|
|
||||||
|
0x48: Start Nation
|
||||||
|
|
||||||
|
0x49: 0xC bytes of 0s
|
6
research/serverInfo.txt
Normal file
6
research/serverInfo.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Server Sheet
|
||||||
|
|
||||||
|
0x00:Server ID Short;
|
||||||
|
0x02:Server Position Short;
|
||||||
|
0x04:Server Pop Short;
|
||||||
|
0x10:Server Name
|
Loading…
Reference in New Issue
Block a user