// 4.1 [PlayerRecord] internal void Load(BinaryReader reader) { #region doc //offset | size/type | Description //-------+-----------+----------------------------------------------------------- //0x0000 | 1 byte | RecordID: // | | 0x00 for game host // | | 0x16 for additional players (see 4.9) //0x0001 | 1 byte | PlayerID //0x0002 | n bytes | PlayerName (null terminated string) // n+2 | 1 byte | size of additional data: // | | 0x01 = custom // | | 0x08 = ladder #endregion //RecordId reader.ReadByte(); playerId = reader.ReadByte(); name = ParserUtility.ReadString(reader); gameType = (GameType)reader.ReadByte(); if (gameType == GameType.Custom) { reader.ReadByte(); } else { //time reader.ReadInt32(); Race = (Race)reader.ReadUInt32(); } // player = player.players[playerId]; }
private void LoadPlayers(BinaryReader reader) { #region 4.0 [Decompressed data] #region doc // # | Size | Name //---+----------+-------------------------- // 1 | 4 byte | Unknown (0x00000110 - another record id?) // 2 | variable | PlayerRecord (see 4.1) // 3 | variable | GameName (null terminated string) (see 4.2) // 4 | 1 byte | Nullbyte // 5 | variable | Encoded String (null terminated) (see 4.3) // | | - GameSettings (see 4.4) // | | - Map&CreatorName (see 4.5) // 6 | 4 byte | PlayerCount (see 4.6) // 7 | 4 byte | GameType (see 4.7) // 8 | 4 byte | LanguageID (see 4.8) // 9 | variable | PlayerList (see 4.9) //10 | variable | GameStartRecord (see 4.11) #endregion //Unknown reader.ReadInt32(); host = new Player(); host.Load(reader); players.Add(host); type = host.GameType; name = ParserUtility.ReadString(reader); //nullbyte reader.ReadByte(); byte[] decoded = ParserUtility.GetDecodedBytes(reader); settings = new GameSettings(decoded); map = new MapInfo(ParserUtility.GetUInt(decoded, 9), ParserUtility.GetString(decoded, 13)); #endregion //playerCount, gameType, langId reader.ReadBytes(12); #region Player List while (reader.PeekChar() == 0x16) { #region doc //offset | size/type | Description //-------+-----------+----------------------------------------------------------- //0x0000 | 4/11 byte | PlayerRecord (see 4.1) //0x000? | 4 byte | unknown // | | (always 0x00000000 for patch version >= 1.07 // | | always 0x00000001 for patch version <= 1.06) #endregion Player p = new Player(); p.Load(reader); players.Add(p); reader.ReadBytes(4); } #endregion #region 4.10 [GameStartRecord] #region doc //offset | size/type | Description //-------+-----------+----------------------------------------------------------- //0x0000 | 1 byte | RecordID - always 0x19 //0x0001 | 1 word | number of data bytes following //0x0003 | 1 byte | nr of SlotRecords following (== nr of slots on startscreen) //0x0004 | n bytes | nr * SlotRecord (see 4.11) // n+4 | 1 dword | RandomSeed (see 4.12) // n+8 | 1 byte | SelectMode // | | 0x00 - team & race selectable (for standard custom games) // | | 0x01 - team not selectable // | | (map setting: fixed alliances in WorldEditor) // | | 0x03 - team & race not selectable // | | (map setting: fixed player properties in WorldEditor) // | | 0x04 - race fixed to random // | | (extended map options: random races selected) // | | 0xcc - Automated Match Making (ladder) // n+9 | 1 byte | StartSpotCount (nr. of start positions in map) #endregion //RecordId, number of data bytes following reader.ReadBytes(3); byte nSlots = reader.ReadByte(); for (byte i = 0; i < nSlots; i++) { #region 4.11 [SlotRecord] #region doc //offset | size/type | Description //-------+-----------+----------------------------------------------------------- //0x0000 | 1 byte | player id (0x00 for computer players) //0x0001 | 1 byte | map download percent: 0x64 in custom, 0xff in ladder //0x0002 | 1 byte | slotstatus: // | | 0x00 empty slot // | | 0x01 closed slot // | | 0x02 used slot //0x0003 | 1 byte | computer player flag: // | | 0x00 for human player // | | 0x01 for computer player //0x0004 | 1 byte | team number:0 - 11 // | | (team 12 == observer or referee) //0x0005 | 1 byte | color (0-11): // | | value+1 matches player colors in world editor: // | | (red, blue, cyan, purple, yellow, orange, green, // | | pink, gray, light blue, dark green, brown) // | | color 12 == observer or referee //0x0006 | 1 byte | player race flags (as selected on map screen): // | | 0x01=human // | | 0x02=orc // | | 0x04=nightelf // | | 0x08=undead // | | 0x20=random // | | 0x40=race selectable/fixed (see notes below) //0x0007 | 1 byte | computer AI strength: (only present in v1.03 or higher) // | | 0x00 for easy // | | 0x01 for normal // | | 0x02 for insane // | | for non-AI players this seems to be always 0x01 //0x0008 | 1 byte | player handicap in percent (as displayed on startscreen) // | | valid values: 0x32, 0x3C, 0x46, 0x50, 0x5A, 0x64 // | | (field only present in v1.07 or higher) #endregion #region byte playerId = reader.ReadByte(); reader.ReadByte(); byte slotStatus = reader.ReadByte(); byte computerFlag = reader.ReadByte(); byte teamNo = reader.ReadByte(); PlayerColor color = (PlayerColor)reader.ReadByte(); Race race = (Race)reader.ReadByte(); if ((uint)race > 0x40) { race -= 0x40; } AIStrength strength = (AIStrength)reader.ReadByte(); int handicap = reader.ReadByte(); #endregion #region if (slotStatus == 0x02) { Player player = GetPlayerById(playerId); if (player == null && computerFlag == 0x01) { player = new Player(); player.Race = race; player.Id = i; if (strength == AIStrength.Easy) { player.Name = "电脑(简单的)"; } else if (strength == AIStrength.Normal) { player.Name = "电脑(中等的)"; } else { player.Name = "电脑(令人发狂的)"; } players.Add(player); } else if (player == null) { continue; } player.SlotNo = i; player.Color = color; player.Handicap = handicap; player.Id = playerId; player.IsComputer = computerFlag == 0x01; player.IsObserver = teamNo == 12; player.Race = race; player.TeamNo = teamNo + 1; } #endregion #endregion } if (buildNo == 0 && nSlots == 0) { #region 6.1 Notes on official Blizzard Replays #region doc //o Since the lack of all slot records, one has to generate these data: //iterate slotNumber from 1 to number of PlayerRecords (see 4.1) // player id = slotNumber // slotstatus = 0x02 (used) // computerflag = 0x00 (human player) // team number = (slotNumber -1) mod 2 (team membership alternates in // PlayerRecord) // color = unknown (player gets random colors) // race = as in PlayerRecord // computerAI = 0x01 (non computer player) // handicap = 0x64 (100%) #endregion foreach (Player player in players) { player.SlotNo = player.Id; player.TeamNo = (player.SlotNo - 1) % 2; player.Handicap = 100; } #endregion } foreach (Player player in players) { Team team = GetTeamByNo(player.TeamNo); team.Add(player); } //random seed, select mode, startspotcount reader.ReadBytes(6); #endregion }
private void LoadActions(BinaryReader reader) { int time = 0; bool isPaused = false; while (reader.BaseStream.Length - reader.BaseStream.Position > 0) { byte blockId = reader.ReadByte(); switch (blockId) { case 0x1A: case 0x1B: case 0x1C: reader.BaseStream.Seek(reader.BaseStream.Position + 4, SeekOrigin.Begin); break; case 0x22: reader.BaseStream.Seek(reader.BaseStream.Position + 5, SeekOrigin.Begin); break; case 0x23: reader.BaseStream.Seek(reader.BaseStream.Position + 10, SeekOrigin.Begin); break; case 0x2F: reader.BaseStream.Seek(reader.BaseStream.Position + 8, SeekOrigin.Begin); break; //leave game case 0x17: reader.ReadInt32(); byte playerId = reader.ReadByte(); Player p = GetPlayerById(playerId); p.Time = time; reader.ReadInt64(); break; //chat case 0x20: byte fromId = reader.ReadByte(); reader.ReadBytes(2); byte chatType = reader.ReadByte(); TalkTo to = TalkTo.All; if (chatType != 0x10) { to = (TalkTo)reader.ReadInt32(); } string message = ParserUtility.ReadString(reader); if (chatType != 0x10) { ChatInfo chat = new ChatInfo(time, GetPlayerById(fromId), to, GetPlayerById((byte)(to - 3)), message); chats.Add(chat); } break; //time slot case 0x1E: case 0x1F: short rest = reader.ReadInt16(); short increasedTime = reader.ReadInt16(); if (!isPaused) { time += increasedTime; } rest -= 2; LoadTimeSlot(reader, rest, time, ref isPaused); break; case 0: return; default: throw new W3gParserException("未知的数据块ID."); } } }