Character deletes now delete the character from the DB instead of just changing the state. DB can now get single character. Character list is sent properly as per how 1.0 did it (only 1 'NEW' entry if available). Character info is now loaded from the new character packet and stored. It is also loaded for each character, encoded, and displayed (still testing).

This commit is contained in:
Filip Maj 2015-09-13 11:30:33 -04:00
parent aadca3968d
commit b717f6aeb1
7 changed files with 402 additions and 100 deletions

View File

@ -128,17 +128,31 @@ namespace FFXIVClassic_Lobby_Server
}
}
public static void renameCharacter(uint characterId, String newName)
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();
MySqlCommand cmd = new MySqlCommand();
//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 WHERE id=@cid";
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();
@ -152,6 +166,8 @@ namespace FFXIVClassic_Lobby_Server
{
conn.Dispose();
}
return false;
}
}
@ -164,7 +180,7 @@ namespace FFXIVClassic_Lobby_Server
conn.Open();
MySqlCommand cmd = new MySqlCommand();
cmd.Connection = conn;
cmd.CommandText = "UPDATE characters SET state=1 WHERE id=@cid AND name=@name";
cmd.CommandText = "DELETE FROM characters WHERE id=@cid AND name=@name";
cmd.Prepare();
cmd.Parameters.AddWithValue("@cid", characterId);
cmd.Parameters.AddWithValue("@name", name);
@ -244,6 +260,29 @@ namespace FFXIVClassic_Lobby_Server
}
}
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 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)))

View File

@ -157,13 +157,11 @@ namespace FFXIVClassic_Lobby_Server
sendWorldList(client, packet);
sendImportList(client, packet);
sendRetainerList(client, packet);
//sendCharacterList(client, packet);
//BasePacket outgoingPacket = new BasePacket("./packets/getCharsPacket.bin");
BasePacket outgoingPacket = new BasePacket("./packets/getChars_GOOD.bin");
sendCharacterList(client, packet);
/*BasePacket outgoingPacket = new BasePacket("./packets/getChars_GOOD.bin");
outgoingPacket.debugPrintPacket();
BasePacket.encryptPacket(client.blowfish, outgoingPacket);
client.queuePacket(outgoingPacket);
client.queuePacket(outgoingPacket);*/
}
@ -211,9 +209,18 @@ namespace FFXIVClassic_Lobby_Server
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)
@ -231,11 +238,13 @@ namespace FFXIVClassic_Lobby_Server
return;
}
bool alreadyTaken;
switch (code)
{
case 0x01://Reserve
var alreadyTaken = Database.reserveCharacter(client.currentUserId, slot, worldId, name, out pid, out cid);
alreadyTaken = Database.reserveCharacter(client.currentUserId, slot, worldId, name, out pid, out cid);
if (alreadyTaken)
{
@ -257,31 +266,45 @@ namespace FFXIVClassic_Lobby_Server
client.newCharaName = name;
}
Log.info(String.Format("User {0} => Character reserved \"{1}\"", client.currentUserId, charaReq.characterName));
Log.info(String.Format("User {0} => Character reserved \"{1}\"", client.currentUserId, name));
break;
case 0x02://Make
CharaInfo info = new CharaInfo();
CharaInfo info = CharaInfo.getFromNewCharRequest(charaReq.characterInfoEncoded);
Database.makeCharacter(client.currentUserId, client.newCharaCid, info);
pid = 1;
cid = client.newCharaCid;
name = client.newCharaName;
name = client.newCharaName;
Log.info(String.Format("User {0} => Character created \"{1}\"", client.currentUserId, charaReq.characterName));
Log.info(String.Format("User {0} => Character created \"{1}\"", client.currentUserId, name));
break;
case 0x03://Rename
Log.info(String.Format("User {0} => Character renamed \"{1}\"", client.currentUserId, charaReq.characterName));
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, 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, charaReq.characterName));
Log.info(String.Format("User {0} => Retainer renamed \"{1}\"", client.currentUserId, name));
break;
}
@ -296,7 +319,7 @@ namespace FFXIVClassic_Lobby_Server
private void sendWorldList(ClientConnection client, SubPacket packet)
{
List<World> serverList = Database.getServers();
WorldListPacket worldlistPacket = new WorldListPacket(2, serverList);
WorldListPacket worldlistPacket = new WorldListPacket(0, serverList);
List<SubPacket> subPackets = worldlistPacket.buildPackets();
BasePacket basePacket = BasePacket.createPacket(subPackets, true, false);
@ -309,7 +332,7 @@ namespace FFXIVClassic_Lobby_Server
{
List<String> names = Database.getReservedNames(client.currentUserId);
ImportListPacket importListPacket = new ImportListPacket(2, names);
ImportListPacket importListPacket = new ImportListPacket(0, names);
List<SubPacket> subPackets = importListPacket.buildPackets();
BasePacket basePacket = BasePacket.createPacket(subPackets, true, false);
BasePacket.encryptPacket(client.blowfish, basePacket);
@ -320,7 +343,7 @@ namespace FFXIVClassic_Lobby_Server
{
List<Retainer> retainers = Database.getRetainers(client.currentUserId);
RetainerListPacket retainerListPacket = new RetainerListPacket(2, retainers);
RetainerListPacket retainerListPacket = new RetainerListPacket(0, retainers);
List<SubPacket> subPackets = retainerListPacket.buildPackets();
BasePacket basePacket = BasePacket.createPacket(subPackets, true, false);
BasePacket.encryptPacket(client.blowfish, basePacket);
@ -331,7 +354,10 @@ namespace FFXIVClassic_Lobby_Server
{
List<Character> characterList = Database.getCharacters(client.currentUserId);
CharacterListPacket characterlistPacket = new CharacterListPacket(2, characterList, 2);
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);

View File

@ -3,6 +3,8 @@ 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
{
@ -11,30 +13,33 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
public uint tribe = 0;
public uint size = 0;
public uint voice = 0;
public uint skinColor = 0;
public ushort skinColor = 0;
public uint hairStyle = 0;
public uint hairColor = 0;
public uint eyeColor = 0;
public ushort hairStyle = 0;
public ushort hairColor = 0;
public ushort hairHighlightColor = 0;
public ushort eyeColor = 0;
public ushort characteristicsColor = 0;
public uint faceType = 0;
public uint faceBrow = 0;
public uint faceEye = 0;
public uint faceIris = 0;
public uint faceEyebrow = 0;
public uint faceEyeShape = 0;
public uint faceEyeSize = 0;
public uint faceNose = 0;
public uint faceMouth = 0;
public uint faceJaw = 0;
public uint faceCheek = 0;
public uint faceOption1 = 0;
public uint faceOption2 = 0;
public uint faceFeatures = 0;
public uint characteristics = 0;
public uint ears = 0;
public uint guardian = 0;
public uint birthMonth = 0;
public uint birthDay = 0;
public uint currentClass = 0;
public uint currentJob = 0;
public uint allegiance = 0;
public uint weapon1 = 0;
public uint weapon2 = 0;
public uint mainHand = 0;
public uint offHand = 0;
public uint headGear = 0;
public uint bodyGear = 0;
@ -46,12 +51,213 @@ namespace FFXIVClassic_Lobby_Server.dataobjects
public uint leftEarGear = 0;
public uint rightFingerGear = 0;
public uint leftFingerGear = 0;
public byte[] toBytes()
public uint currentLevel = 1;
public static CharaInfo getFromNewCharRequest(String encoded)
{
byte[] bytes = new byte[0x120];
return bytes;
byte[] data = Convert.FromBase64String(encoded.Replace('-', '+').Replace('_', '/'));
CharaInfo info = new CharaInfo();
using (MemoryStream stream = new MemoryStream(data))
{
using (BinaryReader reader = new BinaryReader(stream))
{
uint version = reader.ReadUInt32();
uint unknown1 = reader.ReadUInt32();
info.tribe = reader.ReadByte();
info.size = reader.ReadByte();
info.hairStyle = reader.ReadUInt16();
info.hairHighlightColor = reader.ReadUInt16();
info.faceType = reader.ReadByte();
info.characteristics = reader.ReadByte();
info.characteristicsColor = reader.ReadByte();
reader.ReadUInt32();
info.faceEyebrow = reader.ReadByte();
info.faceEyeSize = reader.ReadByte();
info.faceEyeShape = reader.ReadByte();
info.faceNose = reader.ReadByte();
info.faceFeatures = reader.ReadByte();
info.faceMouth = reader.ReadByte();
info.ears = reader.ReadByte();
info.hairColor = reader.ReadUInt16();
reader.ReadUInt32();
info.skinColor = reader.ReadUInt16();
info.eyeColor = reader.ReadUInt16();
info.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.allegiance = reader.ReadByte();
}
}
return info;
}
public String buildForCharaList(Character chara)
{
byte[] data;
using (MemoryStream stream = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(stream))
{
string location1 = "prv0Inn01";
string location2 = "defaultTerritory";
writer.Write((UInt32)0x000004c0);
writer.Write((UInt32)0x232327ea);
writer.Write((UInt32)System.Text.Encoding.UTF8.GetBytes(chara.name).Length);
writer.Write(System.Text.Encoding.UTF8.GetBytes(chara.name));
writer.Write((UInt32)0x1c);
writer.Write((UInt32)0x04);
writer.Write((UInt32)getTribeModel());
writer.Write((UInt32)size);
uint colorVal = skinColor | (uint)(hairColor << 10) | (uint)(eyeColor << 20);
writer.Write((UInt32)colorVal);
writer.Write((UInt32)0x14d00100); //FACE, Figure this out!
uint hairVal = hairHighlightColor | (uint)(hairStyle << 10) | (uint)(characteristicsColor << 20);
writer.Write((UInt32)hairVal);
writer.Write((UInt32)voice);
writer.Write((UInt32)mainHand);
writer.Write((UInt32)offHand);
writer.Write((UInt32)0);
writer.Write((UInt32)0);
writer.Write((UInt32)0);
writer.Write((UInt32)0);
writer.Write((UInt32)0);
writer.Write((UInt32)headGear);
writer.Write((UInt32)bodyGear);
writer.Write((UInt32)legsGear);
writer.Write((UInt32)handsGear);
writer.Write((UInt32)feetGear);
writer.Write((UInt32)waistGear);
writer.Write((UInt32)0);
writer.Write((UInt32)rightEarGear);
writer.Write((UInt32)leftEarGear);
writer.Write((UInt32)0);
writer.Write((UInt32)0);
writer.Write((UInt32)rightFingerGear);
writer.Write((UInt32)leftFingerGear);
for (int i = 0; i < 0xC; i++)
writer.Write((byte)0);
writer.Write((UInt32)1);
writer.Write((UInt32)1);
writer.Write((byte)currentClass);
writer.Write((UInt16)currentLevel);
writer.Write((byte)currentJob);
writer.Write((UInt16)1);
writer.Write((byte)tribe);
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)guardian);
writer.Write((byte)birthMonth);
writer.Write((byte)birthDay);
writer.Write((UInt32)4);
writer.Write((UInt32)4);
writer.BaseStream.Seek(0x10, SeekOrigin.Current);
writer.Write((UInt32)allegiance);
writer.Write((UInt32)allegiance);
}
data = stream.GetBuffer();
File.WriteAllBytes("./packets/out.bin",data);
}
return Convert.ToBase64String(data).Replace('+', '-').Replace('/', '_');
}
public static String debug()
{
byte[] bytes = File.ReadAllBytes("./packets/charaInfo.bin");
Console.WriteLine(Utils.ByteArrayToHex(bytes));
return Convert.ToBase64String(bytes).Replace('+', '-').Replace('/', '_');
}
public UInt32 getTribeModel()
{
switch (tribe)
{
//Hyur Midlander Male
case 0:
default:
return 1;
//Hyur Midlander Female
case 1:
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;
}
}
}
}

View File

@ -20,13 +20,7 @@ namespace FFXIVClassic_Lobby_Server
public bool doRename;
public uint currentZoneId;
public static String characterToEncoded(CharaInfo chara)
{
String charaInfo = System.Convert.ToBase64String(chara.toBytes());
charaInfo.Replace("+", "-");
charaInfo.Replace("/", "_");
return charaInfo;
}
public static CharaInfo EncodedToCharacter(String charaInfo)
{

View File

@ -1,4 +1,5 @@
using FFXIVClassic_Lobby_Server.dataobjects;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
@ -14,20 +15,20 @@ namespace FFXIVClassic_Lobby_Server.packets
public const ushort MAXPERPACKET = 2;
private ulong sequence;
private ushort maxChars;
private List<Character> characterList;
public CharacterListPacket(ulong sequence, List<Character> characterList, ushort maxChars)
public CharacterListPacket(ulong sequence, List<Character> characterList)
{
this.sequence = sequence;
this.characterList = characterList;
this.maxChars = maxChars;
}
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;
@ -44,9 +45,8 @@ namespace FFXIVClassic_Lobby_Server.packets
//Write List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(characterList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
//binWriter.Write((byte)1);
binWriter.Write(maxChars - totalCount <= MAXPERPACKET ? (UInt32)(maxChars - totalCount) : (UInt32)MAXPERPACKET);
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);
}
@ -68,13 +68,16 @@ namespace FFXIVClassic_Lobby_Server.packets
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)0xF4); //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("wAQAAOonIyMNAAAAV3Jlbml4IFdyb25nABwAAAAEAAAAAwAAAAMAAAA_8OADAAHQFAAEAAABAAAAABTQCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGEgAAAAMQAAQCQAAMAsAACKVAAAAPgCAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAkAwAAAAAAAAAAANvb1M05AQAABBoAAAEABqoiIuIKAAAAcHJ2MElubjAxABEAAABkZWZhdWx0VGVycml0b3J5AAwJAhcABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAA="); //Appearance Data
CharaInfo info = JsonConvert.DeserializeObject<CharaInfo>(chara.charaInfo);
//binWriter.Write(info.buildForCharaList(chara)); //Appearance Data
binWriter.Write(CharaInfo.debug()); //Appearance Data
characterCount++;
totalCount++;
@ -90,21 +93,24 @@ namespace FFXIVClassic_Lobby_Server.packets
characterCount = 0;
}
//Incase DB came back with more than max
if (totalCount >= 8)
break;
}
//Keep creating empty slots until done max characters
while (maxChars - totalCount > 0)
//Add a 'NEW' slot if there is space
if (characterList.Count < 8)
{
if (characterCount % MAXPERPACKET == 0)
{
memStream = new MemoryStream(0x3D0);
memStream = new MemoryStream(0x3B0);
binWriter = new BinaryWriter(memStream);
//Write List Info
binWriter.Write((UInt64)sequence);
binWriter.Write(maxChars - totalCount <= MAXPERPACKET ? (byte)(maxChars + 1) : (byte)0);
//binWriter.Write((byte)1);
binWriter.Write(maxChars - totalCount <= MAXPERPACKET ? (UInt32)(maxChars - totalCount) : (UInt32)MAXPERPACKET);
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);
}
@ -115,7 +121,6 @@ namespace FFXIVClassic_Lobby_Server.packets
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
@ -136,22 +141,8 @@ namespace FFXIVClassic_Lobby_Server.packets
}
//If there is anything left that was missed or the list is empty
if (characterCount > 0 || maxChars == 0)
{
if (maxChars == 0)
{
memStream = new MemoryStream(0x3D0);
binWriter = new BinaryWriter(memStream);
//Write Empty List Info
binWriter.Write((UInt64)sequence);
byte listTracker = (byte)((MAXPERPACKET * 2) * subPackets.Count);
binWriter.Write(characterList.Count - totalCount <= MAXPERPACKET ? (byte)(listTracker + 1) : (byte)(listTracker));
binWriter.Write((UInt32)0);
binWriter.Write((byte)0);
binWriter.Write((UInt16)0);
}
if (characterCount > 0 || numCharacters == 0)
{
byte[] data = memStream.GetBuffer();
binWriter.Dispose();
memStream.Dispose();

View File

@ -3,18 +3,18 @@
0x000: Int32;
0x004: Int32;
0x008:Name Size Int32;
0x008:Name Size Int32;
0x00C:Name String; Variable size, but in file the name "Wrenix Wrong" is 0xD in size
0x019: Int32;
0x01D: Int32;
0x021:Tribe Model Int32;
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 Model Int32;
0x029:Colors Info Int32;
0x02D:Face Info Int32;
0x031:Hair Style + Highlight Color Int32;
0x035:Voice Int32;
0x039:MainHand Int32;
0x03D:OffHand Int32;
0x039:MainHand Int32;
0x03D:OffHand Int32;
0x041: Int32;
0x045: Int32;
0x049: Int32;
@ -27,29 +27,37 @@
0x065:Feet Int32;
0x069:Waist Int32;
0x06D: Int32;
0x071:Right Ear Int32;
0x075:Left Ear Int32;
0x071:Right Ear Int32;
0x075:Left Ear Int32;
0x079: Int32;
0x07D: Int32;
0x081:Right Ring Int32;
0x085:Left Ring 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.
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.
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;
0x0C7:Guardian Byte;
0x0C8:Birth Month Byte;
0x0C9:Birth Day Byte;
0x0CA: Short;
0x0CC: Int32;
0x0D0: Int32;
0x0E4: Byte;
0x0E8:Allegiance Byte;
0x0E8:Allegiance Byte;

View 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