private static ICollection<Sc2ReplayAttribute> ParseAttributes(IMpqArchive replay) { var file = replay.ReadFile("replay.attributes.events"); using (var stream = new MemoryStream(file)) { stream.Skip(5); // there is a 5-byte header; should we validate this? var attributeCount = stream.ReadInt32(); var attributes = new List<Sc2ReplayAttribute>(); for (var i = 0; i < attributeCount; i++) { var attr = new Sc2ReplayAttribute { Header = stream.ReadInt32(), Type = (Sc2ReplayAttributeType) stream.ReadInt32(), PlayerIndex = stream.ReadByte(), Value = stream.ReadBytes(4).Reverse().ToArray(), }; attributes.Add(attr); } return attributes; } }
private static Sc2Replay ParseReplay(IMpqArchive archive) { if (archive == null) throw new ArgumentNullException("archive"); var result = new Sc2Replay(); ParseUserDataHeader(archive, result); dynamic details = archive.ReadSerializedData("replay.details", false); result.MapName = Encoding.UTF8.GetString(details[1]); // parse the date, subtract the time zone to get UTC time var timeZoneOffset = new TimeSpan((long)details[6]); var date = DateTime.FromFileTime((long)details[5]); var adjustedDate = date.Subtract(timeZoneOffset); result.DatePlayed = DateTime.SpecifyKind(adjustedDate, DateTimeKind.Utc); result.Players = ParsePlayers(details); ParseMapInformation(details, result); // hack: we don't actually parse the replay gateway since I // haven't been able to find it in the replay file yet. // for now, we just assume that it's the same as the gateway // of the map file resource. result.Gateway = result.MapGateway; result.Attributes = ParseAttributes(archive); ApplyAttributes(result); return result; }
public static byte[]? ReadFile(this IMpqArchive archive, string path) { var size = archive.GetFileSize(path); if (!size.HasValue) { return(null); } var buf = new byte[size.Value]; archive.ReadFile(buf, path); return(buf); }
public static PooledArray <byte>?ReadFilePool(this IMpqArchive archive, string path) { var size = archive.GetFileSize(path); if (!size.HasValue) { return(null); } var buf = new PooledArray <byte>(size.Value); archive.ReadFile(buf.AsArray(), path); return(buf); }
private bool TryOpenMpq(out IMpqArchive m) { try { m = mpqService.Open(); return(true); } catch (Exception e) { messageBoxService.ShowDialog(new MessageBoxFactory <bool>() .SetTitle("Invalid MPQ") .SetMainInstruction("Couldn't parse game MPQ.") .SetContent(e.Message + "\n\nAre you using modified game files?") .WithButton("Ok", false) .Build()); m = null; return(false); } }
public static object ReadSerializedData(this IMpqArchive archive, string path, bool convertStringsToUtf8) { if (archive == null) { throw new ArgumentNullException("archive"); } var file = archive.ReadFile(path); if (file == null) { return(null); } using (var memory = new MemoryStream(file)) using (var reader = new BinaryReader(memory)) { return(Deserialize(reader, convertStringsToUtf8)); } }
private static void ParseUserDataHeader(IMpqArchive replay, Sc2Replay result) { dynamic data = Starcraft2SerializedData.Deserialize(replay.UserDataHeader.UserData, true); var starcraft2 = (string)data[0]; if (!starcraft2.StartsWith("StarCraft II replay")) // todo: this is kind of a hack, for some reason there are extra funky characters after "replay". Why? throw new InvalidOperationException( String.Format( "This does not appear to be a SC2 replay file. (Unexpected user header string: '{0}')", starcraft2)); var buildAndVersion = (IDictionary<long, object>)data[1]; result.Version = new[] { (long)buildAndVersion[0], (long)buildAndVersion[1], (long)buildAndVersion[2], (long)buildAndVersion[3] }; result.Build = new[] { (long)buildAndVersion[4], (long)buildAndVersion[5] }; ValidateBuild(result.Build); var lengthInTicks = (long)data[3]; result.Length = TimeSpan.FromSeconds(lengthInTicks / 16d); }