/// <summary> /// Constructor for playback. /// </summary> /// <param name="fileName">Relative or absolute path (including file name).</param> /// <param name="readMetaData">If true, will read data like Tibia version, recorder version and duration.</param> /// <param name="readPackets">If true, will buffer all packets to memory.</param> /// <param name="readMouseMovements">If true, will try to read a mouse movement file (*.kcammouse) with the same file name as the recording.</param> internal Recording(string fileName, bool readMetaData, bool readPackets, bool readMouseMovements) { this.Type = RecordingType.Playback; if (File.Exists(fileName)) { this.Packets = new List <Recording.Packet>(); if (fileName.EndsWith(".kcam")) { Recorder = Enums.Recorder.TibianicTools; } else if (fileName.EndsWith(".tmv")) { Recorder = Enums.Recorder.TibiaMovie; } else if (fileName.EndsWith(".cam")) { Recorder = Enums.Recorder.IryontCam; } else { return; } FileName = fileName; RecorderVersion = "?"; TibiaVersion = "?"; Duration = 0; //if (fileName.Contains("\\")) { FileNameShort = fileName.Substring(fileName.LastIndexOf('\\') + 1); } //else { FileNameShort = fileName; } if (readMetaData || readPackets || readMouseMovements) { switch (this.Recorder) { case Enums.Recorder.IryontCam: try { using (FileStream fstream = File.OpenRead(this.FileName)) { using (BinaryReader reader = new BinaryReader(fstream)) { if (readPackets) { uint tickFirst = 0; // add first packet tickFirst = (uint)reader.ReadUInt64(); ushort len = reader.ReadUInt16(); byte[] data = new byte[len + 2]; Array.Copy(BitConverter.GetBytes(len), data, 2); byte[] packet = reader.ReadBytes(len); Array.Copy(packet, 0, data, 2, packet.Length); this.Packets.Add(new Recording.Packet(0, data)); while (reader.BaseStream.Position < reader.BaseStream.Length) { uint tick = (uint)reader.ReadUInt64() - tickFirst; len = reader.ReadUInt16(); data = new byte[len + 2]; Array.Copy(BitConverter.GetBytes(len), data, 2); packet = reader.ReadBytes(len); Array.Copy(packet, 0, data, 2, packet.Length); this.Packets.Add(new Recording.Packet(tick, data)); } Recording.Packet lastPacket = this.Packets[this.Packets.Count - 1]; this.Duration = lastPacket.Time; } } } } catch { this.isCorrupt = true; } break; case Enums.Recorder.TibiaMovie: /*using (FileStream stream = File.OpenRead(fileName)) * { * if (readMetaData) * { * byte[] metaData = new byte[8]; * stream.Read(metaData, 0, 8); * metaData = Utils.TibiaCam.Zlib.Decompress(metaData); * RecorderVersion = BitConverter.ToUInt16(metaData, 0).ToString(); * TibiaVersion = BitConverter.ToUInt16(metaData, 2).ToString(); * Duration = BitConverter.ToUInt32(metaData, 4); * } * if (readPackets) * { * byte[] buffer = new byte[stream.Length - 8]; * if (!readMetaData) stream.Position += 8; * stream.Read(buffer, 0, buffer.Length); * Utils.TibiaCam.Zlib.Decompress(buffer); * Packets = Utils.TibiaCam.Zlib.BytesToPacket(Utils.TibiaCam.Zlib.Decompress(buffer)); * } * stream.Close(); * }*/ break; case Enums.Recorder.TibianicTools: if (readMouseMovements) { string mouseFileName = fileName + "mouse"; if (File.Exists(mouseFileName)) { try { this.MouseRecording = new MouseRecording(mouseFileName); } catch { this.MouseRecording = null; } } } try { Stream stream = Utils.TibiaCam.DecompressCamToStream(fileName); int firstByte = stream.ReadByte(); stream.Position = 0; if (firstByte == (int)'T') // true for 1.2 and older { isOld = true; List <string> strings = new List <string>(); StreamReader reader = new StreamReader(stream); TibiaVersion = reader.ReadLine().Replace("TibiaVersion=", ""); RecorderVersion = reader.ReadLine().Replace("TibiaCamVersion=", ""); Duration = (uint)double.Parse(reader.ReadLine().Replace("TotalRunningTime=", "")); if (readPackets) { while (true) { string line = reader.ReadLine(); if (line == null || line.Length == 0) { break; } strings.Add(line); } stream.Close(); stream.Dispose(); List <Recording.Packet> packets = new List <Recording.Packet>(); foreach (string line in strings) { string temp = line; uint sleep = uint.Parse(temp.Substring(0, temp.IndexOf(':'))); temp = temp.Remove(0, temp.IndexOf(':') + 1); string[] split = temp.Split(' '); Objects.Packet packet = new Objects.Packet(); for (int j = 0; j < split.Length; j++) { packet.AddByte(byte.Parse(split[j], System.Globalization.NumberStyles.AllowHexSpecifier)); } packet.GetPosition = 0; packets.Add(new Recording.Packet(sleep, packet.ToBytes())); } Packets = packets; } } else { isOld = false; BinaryReader reader = new BinaryReader(stream); byte[] metadata = new byte[8]; // 2 bytes TibiaVersion, 2 bytes CamVersion, 4 bytes RunningLength(ms) reader.Read(metadata, 0, 8); // fill metadata if (readMetaData) { TibiaVersion = metadata[0] + "." + metadata[1]; RecorderVersion = metadata[2] + "." + metadata[3]; Duration = BitConverter.ToUInt32(metadata, 4); } if (readPackets) { List <Recording.Packet> packets = new List <Recording.Packet>(); while (reader.BaseStream.Position < reader.BaseStream.Length) { uint sleep = reader.ReadUInt32(); uint len = reader.ReadUInt32(); // should be changed to UInt16 in future versions as packets never exceed 65k bytes // merge split packets ushort packetLen = reader.ReadUInt16(); if (packetLen > len - 2) { Objects.Packet p = new Objects.Packet(); reader.BaseStream.Position += 4; p.AddUInt16(packetLen); p.AddBytes(reader.ReadBytes((int)len - 2)); uint totalBytesRead = len - 2; while (totalBytesRead < packetLen) { reader.ReadUInt32(); // sleep, not needed len = reader.ReadUInt32(); p.AddBytes(reader.ReadBytes((int)len)); totalBytesRead += len; } packets.Add(new Recording.Packet(sleep, p.ToBytes())); } else { reader.BaseStream.Position -= 2; packets.Add(new Recording.Packet(sleep, reader.ReadBytes((int)len))); } } Packets = packets; // if duration is 0, get duration from last packet if (this.Duration == 0) { Recording.Packet p = this.Packets[this.Packets.Count - 1]; this.Duration = p.Time; } } reader.Close(); stream.Close(); stream.Dispose(); if (readMouseMovements) { string name = fileName.Substring(0, fileName.LastIndexOf('.')); name += ".kcammouse"; if (File.Exists(name)) { MouseRecording = new MouseRecording(name); } else { MouseRecording = null; } } } } catch { isCorrupt = true; } break; } } } }
internal void Save(string fileName) { if (this.Type != RecordingType.Record) { return; } if (this.Packets.Count == 0) { return; } if (this.MouseRecording != null && this.MouseRecording.IsRecording) { this.MouseRecording.StopRecording(); } if (string.IsNullOrEmpty(fileName)) { fileName = "Unnamed"; int i = 0; while (File.Exists(fileName + "-" + i + ".kcam")) { i++; } fileName += "-" + i + ".kcam"; } else if (File.Exists(fileName)) { File.Delete(fileName); } Recording.Packet lastPacket = Packets[Packets.Count - 1]; this.Duration = lastPacket.Time; //Utils.TibiaCam.CompressCam(Packets, fileName); using (MemoryStream memstream = new MemoryStream()) { string[] split = this.TibiaVersion.Split('.'); memstream.Write(new byte[] { byte.Parse(split[0]), byte.Parse(split[1]) }, 0, 2); // holy shit this is ugly, fucken backwards compability split = this.RecorderVersion.Split('.'); memstream.Write(new byte[] { byte.Parse(split[0]), byte.Parse(split[1]) }, 0, 2); memstream.Write(BitConverter.GetBytes(this.Duration), 0, 4); foreach (Recording.Packet p in this.Packets) { memstream.Write(BitConverter.GetBytes(p.Time), 0, 4); memstream.Write(BitConverter.GetBytes((uint)p.Data.Length), 0, 4); memstream.Write(p.Data, 0, p.Data.Length); } memstream.Position = 0; using (FileStream fstream = File.Create(fileName + (!fileName.EndsWith(".kcam") ? ".kcam" : string.Empty))) { using (DeflateStream compressStream = new DeflateStream(fstream, CompressionMode.Compress)) { compressStream.Write(memstream.ToArray(), 0, (int)memstream.Length); } } } if (this.MouseRecording != null) { string mouseFileName = fileName + "mouse"; this.MouseRecording.Save(mouseFileName); //this.MouseRecording = null; } this.Packets.Clear(); }