/// <summary> /// Decodes the ARZ file. /// </summary> /// <param name="inReader">input BinaryReader</param> /// <param name="baseOffset">Offset in the file.</param> /// <param name="arzFile">ArzFile instance which we are operating.</param> public void Decode(RecordInfo info, BinaryReader inReader, int baseOffset, ArzFile arzFile) { // Record Entry Format // 0x0000 int32 stringEntryID (dbr filename) // 0x0004 int32 string length // 0x0008 string (record type) // 0x00?? int32 offset // 0x00?? int32 length in bytes // 0x00?? int32 timestamp? // 0x00?? int32 timestamp? info.IdStringIndex = inReader.ReadInt32(); info.RecordType = TQData.ReadCString(inReader); info.Offset = inReader.ReadInt32() + baseOffset; // Compressed size // We throw it away and just advance the offset in the file. inReader.ReadInt32(); // Crap1 - timestamp? // We throw it away and just advance the offset in the file. inReader.ReadInt32(); // Crap2 - timestamp? // We throw it away and just advance the offset in the file. inReader.ReadInt32(); // Get the ID string info.ID = arzFile.Getstring(info.IdStringIndex); }
/// <summary> /// Reads the whole string table into memory from a stream. /// </summary> /// <remarks> /// string Table Format /// first 4 bytes is the number of entries /// then /// one string followed by another... /// </remarks> /// <param name="pos">position within the file.</param> /// <param name="reader">input BinaryReader</param> /// <param name="outStream">output StreamWriter.</param> private void ReadStringTable(ArzFile file, int pos, BinaryReader reader, StreamWriter outStream) { reader.BaseStream.Seek(pos, SeekOrigin.Begin); int numstrings = reader.ReadInt32(); file.Strings = new string[numstrings]; if (outStream != null) { outStream.WriteLine("stringTable located at 0x{1:X} numstrings= {0:n0}", numstrings, pos); } for (int i = 0; i < numstrings; ++i) { file.Strings[i] = TQData.ReadCString(reader); if (outStream != null) { outStream.WriteLine("{0},{1}", i, file.Strings[i]); } } }
/// <summary> /// Parses the raw binary data for use within TQVault /// </summary> private void ParseRawData(PlayerCollection pc) { // First create a memory stream so we can decode the binary data as needed. using (MemoryStream stream = new MemoryStream(pc.rawData, false)) { using (BinaryReader reader = new BinaryReader(stream)) { // Find the block pairs until we find the block that contains the item data. int blockNestLevel = 0; int currentOffset = 0; int itemOffset = 0; int equipmentOffset = 0; // vaults start at the item data with no crap bool foundItems = pc.IsVault; bool foundEquipment = pc.IsVault; while ((!foundItems || !foundEquipment) && (currentOffset = FindNextBlockDelim(pc, currentOffset)) != -1) { if (pc.rawData[currentOffset] == beginBlockPattern[0]) { // begin block ++blockNestLevel; currentOffset += beginBlockPattern.Length; // skip past the 4 bytes of noise after begin_block currentOffset += 4; // Seek our stream to the correct position stream.Seek(currentOffset, SeekOrigin.Begin); // Now get the string for pc block string blockName = TQData.ReadCString(reader).ToUpperInvariant(); // Assign loc to our new stream position currentOffset = (int)stream.Position; // See if we accidentally got a begin_block or end_block if (blockName.Equals("BEGIN_BLOCK")) { blockName = "(NONAME)"; currentOffset -= beginBlockPattern.Length; } else if (blockName.Equals("END_BLOCK")) { blockName = "(NONAME)"; currentOffset -= endBlockPattern.Length; } else if (blockName.Equals("ITEMPOSITIONSSAVEDASGRIDCOORDS")) { currentOffset += 4; itemOffset = currentOffset; // skip value for itemPositionsSavedAsGridCoords foundItems = true; } else if (blockName.Equals("USEALTERNATE")) { currentOffset += 4; equipmentOffset = currentOffset; // skip value for useAlternate foundEquipment = true; } } else { // end block --blockNestLevel; currentOffset += endBlockPattern.Length; } } if (foundItems) { try { ParseItemBlock(pc, itemOffset, reader); } catch (ArgumentException exception) { var ex = new ArgumentException($"Error parsing player file Item Block- '{pc.PlayerName}'", exception); Log.ErrorException(ex); throw ex; } try { string outfile = string.Concat(Path.Combine(GamePathResolver.TQVaultSaveFolder, pc.PlayerName), " Export.txt"); using (StreamWriter outStream = new StreamWriter(outfile, false)) { outStream.WriteLine("Number of Sacks = {0}", pc.numberOfSacks); int sackNumber = 0; if (pc.sacks != null) { foreach (SackCollection sack in pc.sacks) { if (!sack.IsEmpty) { outStream.WriteLine(); outStream.WriteLine("SACK {0}", sackNumber); int itemNumber = 0; foreach (Item item in sack) { object[] params1 = new object[20]; params1[0] = itemNumber; params1[1] = ItemProvider.GetFriendlyNames(item).FullNameBagTooltip; params1[2] = item.PositionX; params1[3] = item.PositionY; params1[4] = item.Seed; ////params1[5] = outStream.WriteLine(" {0,5:n0} {1}", params1); itemNumber++; } } sackNumber++; } } } } catch (IOException exception) { Log.ErrorFormat(exception, "Error writing Export file - '{0}' Export.txt" , Path.Combine(GamePathResolver.TQVaultSaveFolder, pc.PlayerName) ); } } // Process the equipment block if (foundEquipment && !pc.IsVault) { try { ParseEquipmentBlock(pc, equipmentOffset, reader); } catch (ArgumentException exception) { var ex = new ArgumentException($"Error parsing player file Equipment Block - '{pc.PlayerName}'", exception); Log.ErrorException(ex); throw ex; } try { string outfile = string.Concat(Path.Combine(GamePathResolver.TQVaultSaveFolder, pc.PlayerName), " Equipment Export.txt"); using (StreamWriter outStream = new StreamWriter(outfile, false)) { if (!pc.EquipmentSack.IsEmpty) { int itemNumber = 0; foreach (Item item in pc.EquipmentSack) { object[] params1 = new object[20]; params1[0] = itemNumber; params1[1] = ItemProvider.GetFriendlyNames(item).FullNameBagTooltip; params1[2] = item.PositionX; params1[3] = item.PositionY; params1[4] = item.Seed; outStream.WriteLine(" {0,5:n0} {1}", params1); itemNumber++; } } } } catch (IOException exception) { Log.ErrorFormat(exception, "Error writing Export file - '{0}' Equipment Export.txt" , Path.Combine(GamePathResolver.TQVaultSaveFolder, pc.PlayerName) ); } } if (pc.IsPlayer) { try { pc.PlayerInfo = ReadPlayerInfo(pc); } catch (ArgumentException ex) { var exx = new ArgumentException($"Error parsing player player info Block - '{pc.PlayerName}'", ex); Log.ErrorException(exx); throw exx; } } } } }
/// <summary> /// Parses the raw binary data for use within TQVault /// </summary> private void ParseRawData(PlayerCollection pc) { // First create a memory stream so we can decode the binary data as needed. using (MemoryStream stream = new MemoryStream(pc.rawData, false)) using (BinaryReader reader = new BinaryReader(stream)) { // Find the block pairs until we find the block that contains the item data. int blockNestLevel = 0; int currentOffset = 0; int itemOffset = 0; int equipmentOffset = 0; // vaults start at the item data with no crap bool foundItems = pc.IsVault; bool foundEquipment = pc.IsVault; while ((!foundItems || !foundEquipment) && (currentOffset = FindNextBlockDelim(pc, currentOffset)) != -1) { if (pc.rawData[currentOffset] == beginBlockPattern[0]) { // begin block ++blockNestLevel; currentOffset += beginBlockPattern.Length; // skip past the 4 bytes of noise after begin_block currentOffset += 4; // Seek our stream to the correct position stream.Seek(currentOffset, SeekOrigin.Begin); // Now get the string for pc block string blockName = TQData.ReadCString(reader).ToUpperInvariant(); // Assign loc to our new stream position currentOffset = (int)stream.Position; // See if we accidentally got a begin_block or end_block if (blockName.Equals("BEGIN_BLOCK")) { blockName = "(NONAME)"; currentOffset -= beginBlockPattern.Length; } else if (blockName.Equals("END_BLOCK")) { blockName = "(NONAME)"; currentOffset -= endBlockPattern.Length; } else if (blockName.Equals("ITEMPOSITIONSSAVEDASGRIDCOORDS")) { currentOffset += 4; itemOffset = currentOffset; // skip value for itemPositionsSavedAsGridCoords foundItems = true; } else if (blockName.Equals("USEALTERNATE")) { currentOffset += 4; equipmentOffset = currentOffset; // skip value for useAlternate foundEquipment = true; } } else { // end block --blockNestLevel; currentOffset += endBlockPattern.Length; } } if (foundItems) { try { ParseItemBlock(pc, itemOffset, reader); } catch (ArgumentException exception) { var ex = new ArgumentException($"Error parsing player file Item Block- '{pc.PlayerName}'", exception); Log.ErrorException(ex); throw ex; } } // Process the equipment block if (foundEquipment && !pc.IsVault) { try { ParseEquipmentBlock(pc, equipmentOffset, reader); } catch (ArgumentException exception) { var ex = new ArgumentException($"Error parsing player file Equipment Block - '{pc.PlayerName}'", exception); Log.ErrorException(ex); throw ex; } } if (pc.IsPlayer) { try { pc.PlayerInfo = ReadPlayerInfo(pc); } catch (ArgumentException ex) { var exx = new ArgumentException($"Error parsing player player info Block - '{pc.PlayerName}'", ex); Log.ErrorException(exx); throw exx; } } } }
/// <summary> /// /// </summary> /// <param name="reader">Reader to player.chr file</param> private void ReadInternal(BinaryReader reader) { _playInfo.Modified = false; var offset = 0; reader.BaseStream.Seek(offset, SeekOrigin.Begin); TQData.ValidateNextString("headerVersion", reader); reader.ReadInt32(); TQData.ValidateNextString("playerCharacterClass", reader); TQData.ReadCString(reader); TQData.ValidateNextString("uniqueId", reader); reader.ReadBytes(16); TQData.ValidateNextString("streamData", reader); var len = reader.ReadInt32(); reader.ReadBytes(len); TQData.ValidateNextString("playerClassTag", reader); var tag = TQData.ReadCString(reader); _playInfo.Class = tag; _playInfo.Money = ReadMoneyValue(reader); _playInfo.DifficultyUnlocked = ReadDifficultyUnlockedValue(reader); TQData.ValidateNextString("hasBeenInGame", reader); _playInfo.HasBeenInGame = reader.ReadInt32(); offset = _playerKeys["CURRENTSTATS.CHARLEVEL"]; reader.BaseStream.Seek(offset, SeekOrigin.Begin); _playInfo.CurrentLevel = reader.ReadInt32(); TQData.ValidateNextString("currentStats.experiencePoints", reader); _playInfo.CurrentXP = reader.ReadInt32(); TQData.ValidateNextString("modifierPoints", reader); _playInfo.AttributesPoints = reader.ReadInt32(); TQData.ValidateNextString("skillPoints", reader); _playInfo.SkillPoints = reader.ReadInt32(); offset = _playerKeys["TEMP"]; reader.BaseStream.Seek(offset, SeekOrigin.Begin); _playInfo.BaseStrength = Convert.ToInt32(reader.ReadSingle()); TQData.ValidateNextString("temp", reader); _playInfo.BaseDexterity = Convert.ToInt32(reader.ReadSingle()); TQData.ValidateNextString("temp", reader); _playInfo.BaseIntelligence = Convert.ToInt32(reader.ReadSingle()); TQData.ValidateNextString("temp", reader); _playInfo.BaseHealth = Convert.ToInt32(reader.ReadSingle()); TQData.ValidateNextString("temp", reader); _playInfo.BaseMana = Convert.ToInt32(reader.ReadSingle()); offset = _playerKeys["PLAYTIMEINSECONDS"]; reader.BaseStream.Seek(offset, SeekOrigin.Begin); _playInfo.PlayTimeInSeconds = reader.ReadInt32(); TQData.ValidateNextString("numberOfDeaths", reader); _playInfo.NumberOfDeaths = reader.ReadInt32(); TQData.ValidateNextString("numberOfKills", reader); _playInfo.NumberOfKills = reader.ReadInt32(); TQData.ValidateNextString("experienceFromKills", reader); _playInfo.ExperienceFromKills = reader.ReadInt32(); TQData.ValidateNextString("healthPotionsUsed", reader); _playInfo.HealthPotionsUsed = reader.ReadInt32(); TQData.ValidateNextString("manaPotionsUsed", reader); _playInfo.ManaPotionsUsed = reader.ReadInt32(); TQData.ValidateNextString("maxLevel", reader); _playInfo.MaxLevel = reader.ReadInt32(); TQData.ValidateNextString("numHitsReceived", reader); _playInfo.NumHitsReceived = reader.ReadInt32(); TQData.ValidateNextString("numHitsInflicted", reader); _playInfo.NumHitsInflicted = reader.ReadInt32(); TQData.ValidateNextString("greatestDamageInflicted", reader); _playInfo.GreatestDamageInflicted = reader.ReadInt32(); TQData.ValidateNextString("(*greatestMonsterKilledName)[i]", reader); _playInfo.GreatestMonster = TQData.ReadUTF16String(reader); TQData.ValidateNextString("(*greatestMonsterKilledLevel)[i]", reader); reader.ReadInt32(); TQData.ValidateNextString("(*greatestMonsterKilledLifeAndMana)[i]", reader); reader.ReadInt32(); TQData.ValidateNextString("(*greatestMonsterKilledName)[i]", reader); _playInfo.GreatestMonster = TQData.ReadUTF16String(reader); TQData.ValidateNextString("(*greatestMonsterKilledLevel)[i]", reader); reader.ReadInt32(); TQData.ValidateNextString("(*greatestMonsterKilledLifeAndMana)[i]", reader); reader.ReadInt32(); TQData.ValidateNextString("(*greatestMonsterKilledName)[i]", reader); _playInfo.GreatestMonster = TQData.ReadUTF16String(reader); TQData.ValidateNextString("(*greatestMonsterKilledLevel)[i]", reader); reader.ReadInt32(); TQData.ValidateNextString("(*greatestMonsterKilledLifeAndMana)[i]", reader); reader.ReadInt32(); TQData.ValidateNextString("criticalHitsInflicted", reader); _playInfo.CriticalHitsInflicted = reader.ReadInt32(); TQData.ValidateNextString("criticalHitsReceived", reader); _playInfo.CriticalHitsReceived = reader.ReadInt32(); }