using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace WoWExtractor { public static class UpdateFieldExtractor { private static string s_File; private static string s_Version; private static string s_Build; private static long s_stringStartOffset; private static long s_dataStartOffset; private static int s_fieldCount; static int m_objectStart; static int m_objectEnd; static int m_unitStart; static int m_unitEnd; static int m_playerStart; static int m_playerEnd; static int m_gameObjectStart; static int m_gameObjectEnd; static int m_itemStart; static int m_itemEnd; static int m_containerStart; static int m_containerEnd; static int m_corpseStart; static int m_corpseEnd; static int m_dynamicObjectStart; static int m_dynamicObjectEnd; private static List m_updateFieldList = new List(); public static bool Extract(string wowExeName, string outputFileName) { if (!File.Exists(wowExeName)) return false; using (FileStream fs = new FileStream(wowExeName, FileMode.Open)) { StreamReader sr = new StreamReader(fs, Encoding.ASCII); s_File = sr.ReadToEnd(); FindStringOffset(); FindDataOffset(); using (BinaryReader br = new BinaryReader(fs)) { FindVersionInfo(br); FillList(br); } FillStartAndEndValues(); AdjustOffsets(); WriteToFile(outputFileName); sr.Close(); } WritePublicFields(); return true; } static void WritePublicFields() { var query = from field in m_updateFieldList where ((field.Flags & FieldFlag.Public) == FieldFlag.Public) select field; foreach (var field in query) { Console.WriteLine("SetPublicField({0}, {1}); // {2}", field.Offset, field.Size, field.Name); } } static void FlagFinder(FieldFlag toSearch) { var query = from field in m_updateFieldList where (field.Flags & toSearch) == toSearch select field; Console.WriteLine("Flag: {0}", toSearch); foreach (var field in query) { Console.WriteLine(field.Name); } } private static void FindVersionInfo(BinaryReader binReader) { // Should be 3839016 for 2.1.3 long startOffset = s_File.IndexOf("RELEASE_BUILD"); if (startOffset > 0) { /* * 6898....2.1.3...RELEASE_BUILD * */ binReader.BaseStream.Position = startOffset - 16; s_Build = binReader.ReadCString(); binReader.BaseStream.Position += 3; s_Version = binReader.ReadCString(); } else { s_Build = "NOTFOUND"; s_Version = "NOTFOUND"; Console.WriteLine("Version Info Not Found!"); } } private static void FindStringOffset() { // Should be 3853356 for 2.1.3 int index = s_File.IndexOf("OBJECT_FIELD_GUID"); if (index > 0) { s_stringStartOffset = index; } else { Console.WriteLine("String Offset Not Found!"); } } private static void FindDataOffset() { byte[] temp = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; StringBuilder sb = new StringBuilder(); foreach (byte b in temp) { sb.Append((char)b); } string toFind = sb.ToString(); // Should be at 6896020 for 2.1.3 int index = s_File.IndexOf(toFind); if (index > 0) { s_dataStartOffset = index - 4; } else { Console.WriteLine("Data Offset Not Found!"); } } private static void FillList(BinaryReader binReader) { s_fieldCount = 0; long stringOffsetDelta = 0x400000; string previousField = String.Empty; string currentField; binReader.BaseStream.Position = s_dataStartOffset; while (true) { UpdateField field = new UpdateField(); field.NameOffset = binReader.ReadUInt32(); if (field.NameOffset < 0x9999) { uint oldNameOffset = field.NameOffset; field.NameOffset = binReader.ReadUInt32(); } field.Offset = binReader.ReadUInt32(); field.Size = binReader.ReadUInt32(); field.Type = (FieldType)binReader.ReadUInt32(); field.Flags = (FieldFlag)binReader.ReadUInt32(); if (s_fieldCount == 0) stringOffsetDelta = field.NameOffset - s_stringStartOffset; long stringOffset = field.NameOffset - stringOffsetDelta; long oldpos = binReader.BaseStream.Position; binReader.BaseStream.Position = stringOffset; currentField = binReader.ReadCString(); binReader.BaseStream.Position = oldpos; StringBuilder sb = new StringBuilder(); sb.AppendFormat("Size: {0} - ", field.Size); sb.AppendFormat("Type: {0} - ", field.Type); sb.AppendFormat("Flags: {0}", field.Flags); field.Description = sb.ToString(); field.Name = currentField; m_updateFieldList.Add(field); s_fieldCount++; if (!previousField.Equals("CORPSE_FIELD_PAD") && currentField.Equals("CORPSE_FIELD_PAD")) break; previousField = currentField; } } private static void FillStartAndEndValues() { for (int i = 0; i < s_fieldCount; i++) { if (m_updateFieldList[i].Name.StartsWith("OBJECT")) { // workaround for GameObjectFields first field being named OBJECT_FIELD_CREATED_BY if (m_updateFieldList[i].Name == "OBJECT_FIELD_CREATED_BY") { m_gameObjectStart = i; m_gameObjectEnd = m_gameObjectStart; m_gameObjectEnd++; continue; } if (m_updateFieldList[i].Offset == 0) { m_objectStart = i; m_objectEnd = m_objectStart; } m_objectEnd++; } if (m_updateFieldList[i].Name.StartsWith("ITEM")) { if (m_updateFieldList[i].Offset == 0) { m_itemStart = i; m_itemEnd = m_itemStart; } m_itemEnd++; continue; } if (m_updateFieldList[i].Name.StartsWith("CONTAINER")) { if (m_updateFieldList[i].Offset == 0) { m_containerStart = i; m_containerEnd = m_containerStart; } m_containerEnd++; continue; } if (m_updateFieldList[i].Name.StartsWith("GAMEOBJECT")) { // Dont need to check for first because first GameObject field is OBJECT_FIELD_CREATED_BY m_gameObjectEnd++; continue; } if (m_updateFieldList[i].Name.StartsWith("UNIT")) { if (m_updateFieldList[i].Offset == 0) { m_unitStart = i; m_unitEnd = m_unitStart; } m_unitEnd++; continue; } if (m_updateFieldList[i].Name.StartsWith("PLAYER")) { if (m_updateFieldList[i].Offset == 0) { m_playerStart = i; m_playerEnd = m_playerStart; } m_playerEnd++; continue; } if (m_updateFieldList[i].Name.StartsWith("CORPSE")) { if (m_updateFieldList[i].Offset == 0) { m_corpseStart = i; m_corpseEnd = m_corpseStart; } m_corpseEnd++; continue; } if (m_updateFieldList[i].Name.StartsWith("DYNAMIC")) { if (m_updateFieldList[i].Offset == 0) { m_dynamicObjectStart = i; m_dynamicObjectEnd = m_dynamicObjectStart; } m_dynamicObjectEnd++; continue; } } } private static void AdjustOffsets() { uint objectDelta = m_updateFieldList[m_objectEnd - 1].Offset + m_updateFieldList[m_objectEnd - 1].Size; uint unitDelta = m_updateFieldList[m_unitEnd - 1].Offset + m_updateFieldList[m_unitEnd - 1].Size; uint itemDelta = m_updateFieldList[m_itemEnd - 1].Offset + m_updateFieldList[m_unitEnd - 1].Size; UpdateField field; for (int i = m_unitStart; i < m_unitEnd; i++) { field = m_updateFieldList[i]; field.Offset += objectDelta; m_updateFieldList[i] = field; } for (int i = m_playerStart; i < m_playerEnd; i++) { field = m_updateFieldList[i]; field.Offset += objectDelta + unitDelta; m_updateFieldList[i] = field; } for (int i = m_itemStart; i < m_itemEnd; i++) { field = m_updateFieldList[i]; field.Offset += objectDelta; m_updateFieldList[i] = field; } for (int i = m_containerStart; i < m_containerEnd; i++) { field = m_updateFieldList[i]; field.Offset += objectDelta + itemDelta; m_updateFieldList[i] = field; } for (int i = m_dynamicObjectStart; i < m_dynamicObjectEnd; i++) { field = m_updateFieldList[i]; field.Offset += objectDelta; m_updateFieldList[i] = field; } for (int i = m_gameObjectStart; i < m_gameObjectEnd; i++) { field = m_updateFieldList[i]; field.Offset += objectDelta; m_updateFieldList[i] = field; } for (int i = m_corpseStart; i < m_corpseEnd; i++) { field = m_updateFieldList[i]; field.Offset += objectDelta; m_updateFieldList[i] = field; } } private static void WriteToFile(string outputFile) { using (StreamWriter sw = new StreamWriter(outputFile)) { UpdateField field; #region Header sw.WriteLine(); sw.WriteLine("// UpdateFields Generated For Build {0}.{1}", s_Version, s_Build); sw.WriteLine(); sw.WriteLine("namespace WCell.Core"); sw.WriteLine("{"); #endregion #region ObjectFields sw.WriteLine("\tpublic enum ObjectFields"); sw.WriteLine("\t{"); for (int i = m_objectStart; i < m_objectEnd; i++) { field = m_updateFieldList[i]; sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t/// {0}", field.Description); sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t{0} = {1},", field.Name, field.Offset); if (i == m_objectEnd - 1) { sw.WriteLine("\t\tOBJECT_END = {0}", field.Offset + field.Size); } } sw.WriteLine("\t}"); sw.WriteLine(); #endregion #region UnitFields sw.WriteLine("\tpublic enum UnitFields"); sw.WriteLine("\t{"); for (int i = m_unitStart; i < m_unitEnd; i++) { field = m_updateFieldList[i]; sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t/// {0}", field.Description); sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t{0} = {1},", field.Name, field.Offset); if (i == m_unitEnd - 1) { sw.WriteLine("\t\tUNIT_END = {0}", field.Offset + field.Size); break; } } sw.WriteLine("\t}"); sw.WriteLine(); #endregion #region PlayerFields sw.WriteLine("\tpublic enum PlayerFields"); sw.WriteLine("\t{"); for (int i = m_playerStart; i < m_playerEnd; i++) { field = m_updateFieldList[i]; sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t/// {0}", field.Description); sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t{0} = {1},", field.Name, field.Offset); if (i == m_playerEnd - 1) { sw.WriteLine("\t\tPLAYER_END = {0}", field.Offset + field.Size); break; } } sw.WriteLine("}"); sw.WriteLine(); #endregion #region ItemFields sw.WriteLine("\tpublic enum ItemFields"); sw.WriteLine("\t{"); for (int i = m_itemStart; i < m_itemEnd; i++) { field = m_updateFieldList[i]; sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t/// {0}", field.Description); sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t{0} = {1},", field.Name, field.Offset); if (i == m_itemEnd - 1) { sw.WriteLine("\t\tITEM_END = {0}", field.Offset + field.Size); break; } } sw.WriteLine("\t}"); sw.WriteLine(); #endregion #region ContainerFields sw.WriteLine("\tpublic enum ContainerFields"); sw.WriteLine("\t{"); for (int i = m_containerStart; i < m_containerEnd; i++) { field = m_updateFieldList[i]; sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t/// {0}", field.Description); sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t{0} = {1},", field.Name, field.Offset); if (i == m_containerEnd - 1) { sw.WriteLine("\t\tCONTAINER_END = {0}", field.Offset + field.Size ); break; } } sw.WriteLine("\t}"); sw.WriteLine(); #endregion #region DynamicObjectFields sw.WriteLine("\tpublic enum DynamicObjectFields"); sw.WriteLine("\t{"); for (int i = m_dynamicObjectStart; i < m_dynamicObjectEnd; i++) { field = m_updateFieldList[i]; sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t/// {0}", field.Description); sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t{0} = {1},", field.Name, field.Offset); if (i == m_dynamicObjectEnd - 1) { sw.WriteLine("\t\tDYNAMICOBJECT_END = {0}", field.Offset + field.Size); break; } } sw.WriteLine("\t}"); sw.WriteLine(); #endregion #region GameObjectFields sw.WriteLine("\tpublic enum GameObjectFields"); sw.WriteLine("\t{"); for (int i = m_gameObjectStart; i < m_gameObjectEnd; i++) { field = m_updateFieldList[i]; sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t/// {0}", field.Description); sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t{0} = {1},", field.Name, field.Offset); if (i == m_gameObjectEnd - 1) { sw.WriteLine("\t\tGAMEOBJECT_END = {0}", field.Offset + field.Size); break; } } sw.WriteLine("\t}"); sw.WriteLine(); #endregion #region CorpseFields sw.WriteLine("\tpublic enum CorpseFields"); sw.WriteLine("\t{"); for (int i = m_corpseStart; i < m_corpseEnd; i++) { field = m_updateFieldList[i]; sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t/// {0}", field.Description); sw.WriteLine("\t\t/// "); sw.WriteLine("\t\t{0} = {1},", field.Name, field.Offset); if (i == m_corpseEnd - 1) { sw.WriteLine("\t\tCORPSE_END = {0}", field.Offset + field.Size); break; } } sw.WriteLine("\t}"); #endregion sw.WriteLine("}"); } } } public struct UpdateField { public uint NameOffset; public uint Offset; public uint Size; public FieldType Type; public FieldFlag Flags; public string Name; public string Description; } [Flags] public enum FieldFlag { Public = 0x0001, // Public? Private = 0x0002, // Private? Flag0x0004 = 0x0004, /// /// Unused /// Flag0x0008 = 0x0008, /// /// ITEM_FIELD_STACK_COUNT /// ITEM_FIELD_DURATION /// ITEM_FIELD_SPELL_CHARGES /// ITEM_FIELD_DURABILITY /// ITEM_FIELD_MAXDURABILITY /// Flag0x0010 = 0x0010, /// /// UNIT_FIELD_MINDAMAGE /// UNIT_FIELD_MAXDAMAGE /// UNIT_FIELD_MINOFFHANDDAMAGE /// UNIT_FIELD_MAXOFFHANDDAMAGE /// UNIT_FIELD_RESISTANCES /// Flag0x0020 = 0x0020, /// /// Only seen in PLAYER_QUEST_LOG_X_1 /// GroupOnly = 0x40, /// /// Unused /// Flag0x0080 = 0x0080, /// /// Mask? /// Flag0x0100 = 0x0100, } public enum FieldType { None, Int32, Unknown, Float, GUID, Bytes, } }