/// <summary> /// Reads a UnitObject from the internal serialised byte array. /// </summary> private void _ReadUnit() { //// start of header // unit object versions _version = _bitBuffer.ReadInt16(); _context = (ObjectContext)_bitBuffer.ReadByte(); if (_version != 0x00BF && _version != 0x00CD && _version != 0x00CF) throw new Exceptions.NotSupportedVersionException("0x00BF or 0x00CD or 0x00CF", "0x" + _version.ToString("X4")); if (_context != ObjectContext.Save && _context != ObjectContext.CharSelect && _context != ObjectContext.CharStats && _context != ObjectContext.ItemDrop) { throw new Exceptions.NotSupportedVersionException("0x00 or 0x02 or 0x03 or 0x04", "0x" + _context.ToString("X2")); } if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("Version = {0} (0x{0:X4}), Context = {1} (0x{2:X2})", _version, _context, (int)_context)); } // content bit fields _bitFieldCount = _bitBuffer.ReadBits(8); if (_bitFieldCount == 1) _bitField = _bitBuffer.ReadUInt32(); if (_bitFieldCount == 2) _bitField = _bitBuffer.ReadUInt64(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("BitField = {0} (0x{1:X16})", _DebugBinaryFormat(_bitField), _bitField)); } // total bit count if (_TestBit(Bits.Bit1DBitCountEof)) { _bitCount = _bitBuffer.ReadBits(32); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("Total BitCount = {0}", _bitCount)); } } // begin data magic word if (_TestBit(Bits.Bit00FlagAlignment)) { _beginFlag = (uint)_bitBuffer.ReadBits(32); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("BeginFlag = 0x{0}", _beginFlag.ToString("X8"))); } if (_beginFlag != ObjectMagicWord && _beginFlag != ItemMagicWord) throw new Exceptions.UnexpectedTokenException(ObjectMagicWord, _beginFlag); } // dunno what these are exactly if (_TestBit(Bits.Bit1CTimeStamps)) { _timeStamp1 = _bitBuffer.ReadBits(32); _timeStamp2 = _bitBuffer.ReadBits(32); _timeStamp3 = _bitBuffer.ReadBits(32); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("TimeStamp1 = {0}, TimeStamp2 = {1}, TimeStamp3 = {2}", _timeStamp1, _timeStamp2, _timeStamp3)); } } // last station visited save/respawn location if (_TestBit(Bits.Bit1FSaveLocations)) { int saveLocationsCount = _bitBuffer.ReadBitsShift(4); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("SaveLocationsCount = {0}", saveLocationsCount)); } for (int i = 0; i < saveLocationsCount; i++) { ushort levelCode = _bitBuffer.ReadUInt16(); // table 0x6D (LEVEL) ushort difficultyCode = _bitBuffer.ReadUInt16(); // table 0xB2 (DIFFICULTY) SaveLocation saveLocation = new SaveLocation { Level = (LevelRow)FileManager.GetRowFromCode(Xls.TableCodes.LEVEL, (short)levelCode), Difficulty = (DifficultyRow)FileManager.GetRowFromCode(Xls.TableCodes.DIFFICULTY, (short)difficultyCode) }; SaveLocations.Add(saveLocation); if ((SaveLocations[i].Level == null && SaveLocations[i].Difficulty != null) || (SaveLocations[i].Level != null && SaveLocations[i].Difficulty == null)) { throw new Exceptions.UnitObjectException(String.Format("Invalid SaveLocation encountered. Level = {0:X4}, Difficulty = {1:X4}", levelCode, difficultyCode)); } if (!_debugOutputLoadingProgress) continue; if (SaveLocations[i].Level == null || SaveLocations[i].Difficulty == null) { Debug.WriteLine(String.Format("SaveLocations[{0}].LevelCode = {1} (0x{1:X4}), SaveLocations[{0}].DifficultyCode = {2} (0x{2:X4})", i, levelCode, difficultyCode)); } else { Debug.WriteLine(String.Format("SaveLocations[{0}].Level = {1}, SaveLocations[{0}].Difficulty = {2}", i, SaveLocations[i].Level.levelName, SaveLocations[i].Difficulty.name)); } } } // character flags if (_TestBit(Bits.Bit20States1)) { int statCount = _bitBuffer.ReadBits(8); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("StateCode1Count = {0}", statCount)); } for (int i = 0; i < statCount; i++) { int state = _bitBuffer.ReadInt16(); AddState1(state); // table 0x4B (STATES) if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("StateCodes1[{0}] = {1}({2:X})", i, StateCodes1[i], (short)(StateCodes1[i]))); } } } //// end of header // bit offsets to bookmarks (only 1 bookmark though - "hotkeys") if (_TestBit(Bits.Bit1BBookmarks)) { BookmarkCount = _bitBuffer.ReadBits(5); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("BookmarkCount = {0}", BookmarkCount)); } if (BookmarkCount > 1) { throw new Exceptions.UnitObjectNotImplementedException("Unexpected BookmarkCount (> 1)!\nNot-Implemented cases. Please report this error and supply the offending file."); } for (int i = 0; i < BookmarkCount; i++) { Bookmark bookmark = new Bookmark { Code = _bitBuffer.ReadUInt16(), Offset = _bitBuffer.ReadInt32() }; if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("Bookmarks[{0}].Code = {1} (0x{1:X4}), Bookmarks[{0}].Offset = {2}", i, bookmark.Code, bookmark.Offset)); } Bookmarks.Add(bookmark); } } // dunno... if (_TestBit(Bits.Bit05Unknown)) { UnitObjectId = _bitBuffer.ReadInt32(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("UnitObjectId = {0} (0x{0:X4})", UnitObjectId)); } } // unit type/code // if unit type == 1, table = 0x91 (PLAYERS) // 2, table = 0x77 (MONSTERS) // 3? (table = 0x72; MISSILES at a guess. For memory, MISSILES doesn't use code values - probably why not seen in ASM) // 4, table = 0x67 (ITEMS) // 5, table = 0x7B (OBJECTS) UnitType = (UnitTypes)_bitBuffer.ReadBits(4); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("UnitType = {0}", UnitType)); } UnitCode = _bitBuffer.ReadUInt16(); if (_debugOutputLoadingProgress) { Debug.Write(String.Format("UnitCode = {0} (0x{0:X4}), ", UnitCode)); } Xls.TableCodes tableCode = Xls.TableCodes.Null; switch (UnitType) { case UnitTypes.Player: tableCode = Xls.TableCodes.PLAYERS; break; case UnitTypes.Monster: tableCode = Xls.TableCodes.MONSTERS; break; case UnitTypes.Missile: tableCode = Xls.TableCodes.MISSILES; break; case UnitTypes.Item: tableCode = Xls.TableCodes.ITEMS; break; case UnitTypes.Object: tableCode = Xls.TableCodes.OBJECTS; break; } if (tableCode == Xls.TableCodes.Null) throw new Exceptions.UnitObjectException("The unit object data has an unknown UnitType."); UnitData = FileManager.GetUnitDataRowFromCode(tableCode, (short)UnitCode); if (UnitData == null) Debug.WriteLine(String.Format("Warning: UnitCode {0} (0x{0:X4}) not found!", UnitCode)); if (_debugOutputLoadingProgress && UnitData != null) { ExcelFile unitDataTable = FileManager.GetExcelTableFromCode(tableCode); String rowName = unitDataTable.ReadStringTable(UnitData.name); Debug.WriteLine(String.Format("UnitDataName = " + rowName)); } // unit object id if (_TestBit(Bits.Bit17ObjectId)) { if (_version > 0xB2) { ObjectId = _bitBuffer.ReadUInt64(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("ObjectId = {0} (0x{0:X16})", ObjectId)); } if (ObjectId == 0) { throw new Exceptions.UnitObjectNotImplementedException("if (ObjectId == 0)"); } } } // item positioning stuff if (_TestBit(Bits.Bit01Unknown) || _TestBit(Bits.Bit03Unknown)) { IsInventory = _bitBuffer.ReadBool(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("IsInventory = {0}, Bits.Bit01Unknown = {1}, Bits.Bit03Unknown = {2}", IsInventory, _TestBit(Bits.Bit01Unknown), _TestBit(Bits.Bit03Unknown))); } if (IsInventory) // item is in inventory { if (_TestBit(Bits.Bit02Unknown)) { Unknown02 = _bitBuffer.ReadBits(32); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("Unknown02 = {0}", Unknown02)); } } InventoryLocationIndex = _bitBuffer.ReadBits(12); InventoryPositionX = _bitBuffer.ReadBits(12); InventoryPositionY = _bitBuffer.ReadBits(12); Unknown04 = _bitBuffer.ReadBits(4); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("InventoryLocationIndex = {0}, InventoryPositionX = {1}, InventoryPositionY = {2}, Unknown04 = {3}", InventoryLocationIndex, InventoryPositionX, InventoryPositionY, Unknown04)); } Unknown0103Int64 = _bitBuffer.ReadNonStandardFunc(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("Unknown0103Int64 = {0} (0x{0:X16})", Unknown0103Int64)); } } else // item is a "world drop" { RoomId = _bitBuffer.ReadInt32(); Position.X = _bitBuffer.ReadFloat(); Position.Y = _bitBuffer.ReadFloat(); Position.Z = _bitBuffer.ReadFloat(); Unknown0103Float21 = _bitBuffer.ReadFloat(); Unknown0103Float22 = _bitBuffer.ReadFloat(); Unknown0103Float23 = _bitBuffer.ReadFloat(); Normal.X = _bitBuffer.ReadFloat(); Normal.Y = _bitBuffer.ReadFloat(); Normal.Z = _bitBuffer.ReadFloat(); Unknown0103Int2 = _bitBuffer.ReadBits(10); Unknown0103Float4 = _bitBuffer.ReadFloat(); Unknown0103Float5 = _bitBuffer.ReadFloat(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("RoomId = {0}", RoomId)); Debug.WriteLine(String.Format("Position.X = {0}, Position.Y = {1}, Position.Z = {2}", Position.X, Position.Y, Position.Z)); Debug.WriteLine(String.Format("Unknown0103Float21 = {0}, Unknown0103Float22 = {1}, Unknown0103Float23 = {2}", Unknown0103Float21, Unknown0103Float22, Unknown0103Float23)); Debug.WriteLine(String.Format("NormalX = {0}, NormalY = {1}, NormalZ = {2}", Normal.X, Normal.Y, Normal.Z)); Debug.WriteLine(String.Format("Unknown0103Int2 = {0}", Unknown0103Int2)); Debug.WriteLine(String.Format("Unknown0103Float4 = {0}", Unknown0103Float4)); Debug.WriteLine(String.Format("Unknown0103Float5 = {0}", Unknown0103Float5)); } } } // I think this has something to do with the Monsters table +46Ch, bit 0x55 = 4 bits or bit 0x47 = 2 bits. Or Objects table +46Ch, bit 0x55 = 2 bits... Something like that if (_TestBit(Bits.Bit06Unknown)) { UnknownBool06 = _bitBuffer.ReadBool(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("UnknownBool06 = {0}", UnknownBool06)); } if (!UnknownBool06) { throw new Exceptions.UnitObjectNotImplementedException("if (UnknownBool06 != 1)"); } } if (_TestBit(Bits.Bit09ItemLookGroup)) { ItemLookGroupCode = _bitBuffer.ReadBits(8); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("ItemLookGroupCode = {0} (0x{0:X2})", ItemLookGroupCode)); } } // on character only if (_TestBit(Bits.Bit07CharacterShape)) { CharacterHeight = _bitBuffer.ReadByte(); CharacterBulk = _bitBuffer.ReadByte(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("CharacterHeight = {0}, CharacterBulk = {1}", CharacterHeight, CharacterBulk)); } } // object id for older versions - they moved it? if (_TestBit(Bits.Bit17ObjectId)) { if (_version <= 0xB2) { throw new Exceptions.UnitObjectNotImplementedException("if (_TestBit(0x17) && Version <= 0xB2)"); } } // on character only if (_TestBit(Bits.Bit08CharacterName)) { int unicodeCharCount = _bitBuffer.ReadBits(8); if (unicodeCharCount > 0) { int byteCount = unicodeCharCount * 2; // is Unicode string without \0 _charNameBytes = new byte[byteCount]; for (int i = 0; i < byteCount; i++) { _charNameBytes[i] = _bitBuffer.ReadByte(); } Name = Encoding.Unicode.GetString(_charNameBytes, 0, byteCount); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("Name = {0}", Name)); } } } // on both character and items - appears to be always zero for items if (_TestBit(Bits.Bit0AStates2)) { int stateCount = _bitBuffer.ReadBits(8); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("StateCode2Count = {0}", stateCount)); } for (int i = 0; i < stateCount; i++) { int state = _bitBuffer.ReadInt16(); AddState2(state); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("StateCodes2[{0}] = {1}({2:X})", i, StateCodes2[i], (short)(StateCodes2[i]))); } // this section looks like it has more reading if Bit14 is flagged (CharSelectStats) } } if (_context > ObjectContext.CharSelect && (_context <= ObjectContext.Unknown6 || _context != ObjectContext.Unknown7)) // so if == 0, 1, 2, 7, then *don't* do this { ContextBool = _bitBuffer.ReadBool(); if (ContextBool) { ContextBoolValue = _bitBuffer.ReadBits(4); // invlocidx?? } if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("UsageBool = {0}, UsageBoolValue = {1}", ContextBool, ContextBoolValue)); } } // <unknown bitfield 0x11th bit> - only seen as false anyways IsDead = _bitBuffer.ReadBool(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("IsDead = {0}", IsDead)); } // unit stats if (_TestBit(Bits.Bit0DStats)) { Stats.ReadStats(_bitBuffer, true); } else if (_TestBit(Bits.Bit14CharSelectStats)) { int characterLevel = _bitBuffer.ReadByte(); // stats row 0x000 (level) Stats.SetStat("level", characterLevel); int characterPvpRankRowIndex = _bitBuffer.ReadByte(); // stats row 0x347 (player_rank) Stats.SetStat("player_rank", characterPvpRankRowIndex); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("LevelRowIndex = {0}, PlayerRankRowIndex = {1}", characterLevel, characterPvpRankRowIndex)); } if (_TestBit(Bits.Bit1ECharSelectStatsMaxDifficulty)) { int maxDifficultyRowIndex = _bitBuffer.ReadBits(3); // stats row 0x347 (difficulty_max) Stats.SetStat("difficulty_max", maxDifficultyRowIndex); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("MaxDifficultyRowIndex = {0}, ", maxDifficultyRowIndex)); } } } HasAppearanceDetails = _bitBuffer.ReadBool(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("HasAppearanceDetails = {0}", HasAppearanceDetails)); } if (HasAppearanceDetails) { _ReadAppearance(); } if (_TestBit(Bits.Bit12Items)) { ItemEndBitOffset = _bitBuffer.ReadInt32(); ItemCount = _bitBuffer.ReadBits(10); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("ItemEndBitOffset = {0}, ItemCount = {1}", ItemEndBitOffset, ItemCount)); } for (int i = 0; i < ItemCount; i++) { UnitObject item = new UnitObject(_bitBuffer, _debugOutputLoadingProgress); item._ReadUnit(); Items.Add(item); } } if (_TestBit(Bits.Bit1AHotkeys)) { HotkeyFlag = _bitBuffer.ReadUInt32(); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("HotkeyFlag = {0} (0x{0:X8})", HotkeyFlag)); } if (HotkeyFlag != HotkeysMagicWord) { throw new Exceptions.UnexpectedTokenException(HotkeysMagicWord, HotkeyFlag); } EndFlagBitOffset = _bitBuffer.ReadBits(32); // to end flag if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("EndFlagBitOffset = {0}", EndFlagBitOffset)); } HotkeyCount = _bitBuffer.ReadBits(6); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("HotkeyCount = {0}", HotkeyCount)); } for (int i = 0; i < HotkeyCount; i++) { Hotkey hotkey = new Hotkey { Code = _bitBuffer.ReadUInt16(), // code from TAG table }; Hotkeys.Add(hotkey); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("hotkey.Code = 0x{0:X4}", hotkey.Code)); } hotkey.UnknownCount = _bitBuffer.ReadBits(4); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("hotkey.UnknownCount = " + hotkey.UnknownCount)); } if (hotkey.UnknownCount > 0x02) { throw new Exceptions.UnitObjectNotImplementedException("if (hotkey.UnknownCount > 0x02)"); } hotkey.UnknownExists = new bool[hotkey.UnknownCount]; hotkey.UnknownValues = new int[hotkey.UnknownCount]; for (int j = 0; j < hotkey.UnknownCount; j++) { hotkey.UnknownExists[j] = _bitBuffer.ReadBool(); if (hotkey.UnknownExists[j]) { hotkey.UnknownValues[j] = _bitBuffer.ReadBits(32); // under some condition this will be ReadFromOtherFunc thingy } if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("hotkey.UnknownExists[{0}] = {1}, hotkey.UnknownValues[{0}] = 0x{2:X8}", j, hotkey.UnknownExists[j], hotkey.UnknownValues[j])); } } hotkey.SkillCount = _bitBuffer.ReadBits(4); hotkey.SkillExists = new bool[hotkey.SkillCount]; hotkey.SkillCode = new int[hotkey.SkillCount]; for (int j = 0; j < hotkey.SkillCount; j++) { hotkey.SkillExists[j] = _bitBuffer.ReadBool(); if (hotkey.SkillExists[j]) { hotkey.SkillCode[j] = _bitBuffer.ReadBits(32); // code from SKILLS table } if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("hotkey.SkillExists[{0}] = {1}, hotkey.SkillCode[{0}] = 0x{2:X8}", j, hotkey.SkillExists[j], hotkey.SkillCode[j])); } } hotkey.UnitTypeCode = _bitBuffer.ReadBits(32); // code from UNITTYPES table if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("hotkey.UnitTypeCode = 0x{0:X8}", hotkey.UnitTypeCode)); } } } // end flag EndFlag = _bitBuffer.ReadBits(32); if (_debugOutputLoadingProgress) { Debug.WriteLine(String.Format("EndFlag = {0} (0x{0:X8})", EndFlag)); } if (EndFlag != _beginFlag && EndFlag != ItemMagicWord) { int bitOffset = _bitCount - _bitBuffer.BitOffset; int byteOffset = (_bitBuffer.Length - _bitBuffer.Offset) - (_bitBuffer.BytesUsed); throw new Exceptions.InvalidFileException("Flags not aligned!\nBit Offset: " + _bitBuffer.BitOffset + "\nExpected: " + _bitCount + " (+" + bitOffset + ")\nBytes Used: " + (_bitBuffer.BytesUsed) + "\nExpected: " + (_bitBuffer.Length - _bitBuffer.Offset) + " (+" + byteOffset + ")"); } if (_TestBit(Bits.Bit1DBitCountEof)) // no reading is done in here { // todo: do check that we're at the EoF bit count etc } }