/// <summary> /// Constructor for playback. /// </summary> /// <param name="fileName"></param> internal MouseRecording(string fileName) { this.FileName = fileName; this.Packets = new List <Objects.Packet>(); if (!File.Exists(fileName)) { return; } using (Stream stream = Utils.TibiaCam.DecompressCamToStream(fileName)) { using (BinaryReader reader = new BinaryReader(stream)) { this._RecorderVersion = reader.ReadUInt16(); this.Interval = reader.ReadUInt16(); WinApi.RECT r = new WinApi.RECT(); r.left = 0; r.top = 0; r.right = reader.ReadUInt16(); r.bottom = reader.ReadUInt16(); this.clientWindowOld = r; WinApi.RECT r2 = new WinApi.RECT(); r2.left = reader.ReadUInt16(); r2.top = reader.ReadUInt16(); r2.right = reader.ReadUInt16(); r2.bottom = reader.ReadUInt16(); this.gameWindowOld = r2; while (reader.BaseStream.Position < reader.BaseStream.Length) { Objects.Packet p = new Objects.Packet(); DataType dt = (DataType)reader.ReadByte(); p.AddByte((byte)dt); switch (dt) { case DataType.ClientWindowChange: p.AddUInt16(reader.ReadUInt16()); // width p.AddUInt16(reader.ReadUInt16()); // height break; case DataType.GameWindowChange: p.AddUInt16(reader.ReadUInt16()); // x p.AddUInt16(reader.ReadUInt16()); // y p.AddUInt16(reader.ReadUInt16()); // width p.AddUInt16(reader.ReadUInt16()); // height break; case DataType.MouseXY: p.AddByte(reader.ReadByte()); // mouse %x p.AddByte(reader.ReadByte()); // mouse %y break; } this.Packets.Add(p); } } } }
private void ThreadRecord() { try { this.Packets = new List <Objects.Packet>(); Objects.Packet meta = new Objects.Packet(); meta.AddUInt16((ushort)Settings.CurrentVersion); // recorder version meta.AddUInt16(Settings.Tibiacam.Recorder.MouseInterval); WinApi.RECT clientRect = new WinApi.RECT(); WinApi.GetClientRect(Client.Tibia.MainWindowHandle, out clientRect); this.clientWindowOld = clientRect; //meta.AddUInt16((ushort)clientRect.left); //meta.AddUInt16((ushort)clientRect.top); meta.AddUInt16((ushort)clientRect.right); meta.AddUInt16((ushort)clientRect.bottom); WinApi.RECT r = Client.Misc.GameWindow; this.gameWindowOld = r; meta.AddUInt16((ushort)r.left); meta.AddUInt16((ushort)r.top); meta.AddUInt16((ushort)r.right); meta.AddUInt16((ushort)r.bottom); this.Packets.Add(meta); this.Interval = Settings.Tibiacam.Recorder.MouseInterval; while (isRecording) { Objects.Packet p; WinApi.RECT newClientRect = new WinApi.RECT(); WinApi.GetClientRect(Client.Tibia.MainWindowHandle, out newClientRect); if (newClientRect.right != this.clientWindowOld.right || newClientRect.bottom != this.clientWindowOld.bottom) { p = new Objects.Packet(); this.clientWindowOld = newClientRect; p.AddByte((byte)DataType.ClientWindowChange); p.AddUInt16((ushort)newClientRect.right); p.AddUInt16((ushort)newClientRect.bottom); this.Packets.Add(p); continue; } WinApi.RECT newRect = Client.Misc.GameWindow; if (this.gameWindowOld.left != newRect.left || this.gameWindowOld.bottom != newRect.bottom) { p = new Objects.Packet(); this.gameWindowOld = newRect; p.AddByte((byte)DataType.GameWindowChange); p.AddUInt16((ushort)newRect.left); p.AddUInt16((ushort)newRect.top); p.AddUInt16((ushort)newRect.right); p.AddUInt16((ushort)newRect.bottom); this.Packets.Add(p); continue; } p = new Objects.Packet(); System.Drawing.Point mousePercentage = this.CalculateMousePercentage(); p.AddByte((byte)DataType.MouseXY); if (WinApi.GetForegroundWindow() == Client.Tibia.MainWindowHandle) { p.AddByte((byte)mousePercentage.X); p.AddByte((byte)mousePercentage.Y); } else { p.AddByte(0); p.AddByte(0); } this.Packets.Add(p); Thread.Sleep(this.Interval); } } catch (Exception ex) { MessageBox.Show("Mouse recording error:\n" + ex.Message + "\n" + ex.StackTrace); } isRecording = false; }
private void PlaybackRecv() { try { NetworkStream nstream = TcpClientLocal.GetStream(); int connection = 0; bool doLoop = true, obtainedXteaKey = false; while (doLoop) { Objects.Packet p = Objects.Packet.GetNextPacket(nstream); if (p.Length == 0) { break; } if (!obtainedXteaKey) { if (Client.TibiaVersion >= 770) { this.XteaKey = new uint[4]; for (int i = 0; i < 4; i++) { this.XteaKey[i] = Memory.ReadUInt(Addresses.Client.XTEAKey + i * 4); } } obtainedXteaKey = true; } connection = Memory.ReadByte(Addresses.Client.Connection); switch ((Enums.Connection)connection) { case Enums.Connection.WaitingForCharacterList: if (this.AutoPlayback) { p = new Objects.Packet(); p.AddByte(0x64); p.AddByte(1); p.AddString("TibiaCam"); p.AddString(CurrentRecording.TibiaVersion); p.AddBytes(IPAddress.Loopback.GetAddressBytes()); p.AddUInt16(this.ListenerPort); p.AddUInt16(0); // premium days p.AddLength(); if (Client.TibiaVersion >= 770) { p.XteaEncrypt(this.XteaKey); p.AddLength(); } p.Send(this.TcpClientLocal); doLoop = false; break; } List <string> files = new List <string>(); foreach (string kcam in System.IO.Directory.GetFiles(System.Windows.Forms.Application.StartupPath + "\\", "*.kcam")) { files.Add(kcam); } foreach (string iryontcam in System.IO.Directory.GetFiles(System.Windows.Forms.Application.StartupPath + "\\", "*.cam")) { files.Add(iryontcam); } if (Settings.Tibiacam.Playback.SupportTibiaMovies) { foreach (string tmv in System.IO.Directory.GetFiles(System.Windows.Forms.Application.StartupPath + "\\", "*.tmv")) { files.Add(tmv); } } files.Sort(); string warning = string.Empty; int count = files.Count; if (files.Count > 255) { warning = "\n\nWarning! There are " + files.Count + " recordings.\nOnly the first 255 recordings are listed."; count = 255; } p = new Objects.Packet(); p.AddByte(0x14); // motd type // motd id is 0-255, which is followed by a newline (\n) p.AddString((byte)new Random().Next(255) + "\nThank you for using Tibianic Tools.\nhttp://code.google.com/p/tibianic-tools/\nhttp://tibianic.org/" + warning); p.AddByte(0x64); // character list type List <Recording> recordings = new List <Recording>(); for (int i = 0; i < count; i++) { Recording r = new Recording(files[i], Settings.Tibiacam.Playback.ReadMetaData, false, false); if (!r.isCorrupt) { recordings.Add(r); } } p.AddByte((byte)count); // amount of characters foreach (Recording r in recordings) { p.AddString(r.FileNameShort); // character name p.AddString(r.TibiaVersion); // server name p.AddBytes(IPAddress.Loopback.GetAddressBytes()); // server ipv4 (in this case: 127.0.0.1) p.AddUInt16(this.ListenerPort); // server port } p.AddUInt16((ushort)count); // premium days p.AddLength(); if (Client.TibiaVersion >= 770) { p.XteaEncrypt(this.XteaKey); p.AddLength(); } p.Send(TcpClientLocal); doLoop = false; break; case Enums.Connection.ConnectingGameServer: if (this.AutoPlayback) { this.threadPlaybackSend = new Thread(new ThreadStart(PlaybackSend)); this.threadPlaybackSend.Start(); break; } CharacterList.Player player = CharacterList.GetPlayers()[Memory.ReadByte(Addresses.Charlist.SelectedIndex)]; if (!System.IO.File.Exists(player.Name)) { System.Windows.Forms.MessageBox.Show(player.Name + "\ndoes not exist", "Error"); doLoop = false; break; } Recording rec = new Recording(player.Name, true, true, Settings.Tibiacam.Playback.doPlayMouse); if (rec.isCorrupt || !rec.ContainsData()) { System.Windows.Forms.MessageBox.Show(player.Name + "\nappears to be corrupt or it doesn't contain any data", "Error"); doLoop = false; break; } this.CurrentRecording = rec; while (this.threadPlaybackSend != null && this.threadPlaybackSend.IsAlive) { this.threadPlaybackSend.Abort(); Thread.Sleep(100); } while (this.threadPlaybackSendMouse != null && this.threadPlaybackSendMouse.IsAlive) { this.threadPlaybackSendMouse.Abort(); Thread.Sleep(100); } this.threadPlaybackSend = new Thread(new ThreadStart(PlaybackSend)); this.threadPlaybackSend.Start(); break; case Enums.Connection.Online: ushort len = p.GetUInt16(); if (Client.TibiaVersion >= 770) { p = p.XteaDecrypt(this.XteaKey, 2); if (p == null) { break; } p.GetPosition = 2; } byte type = p.GetByte(); switch (type) { case 0x14: // logout while (this.threadPlaybackSend != null && this.threadPlaybackSend.IsAlive) { this.threadPlaybackSend.Abort(); Thread.Sleep(100); } this.TcpClientLocal.Close(); return; case 0x96: // player speech byte speechType = p.GetByte(); if (speechType < 1 || speechType > 3) { break; } string msg = p.GetString().ToLower(); string[] msgSplit = msg.Split(' '); p = new Objects.Packet(); switch (msgSplit[0]) { case "info": p.AddByte(0xb4); p.AddByte(22); p.AddString("Tibia version: " + this.CurrentRecording.TibiaVersion + "\nRecorder version: " + this.CurrentRecording.RecorderVersion + "\n# of packets: " + this.CurrentRecording.Packets.Count); p.AddLength(); if (Client.TibiaVersion >= 770) { p.XteaEncrypt(this.XteaKey, p.GetPosition); p.AddLength(); } p.Send(this.TcpClientLocal); break; case "goto": TimeSpan ts; if (!TimeSpan.TryParse(msgSplit[1], out ts)) { break; } this.FastForward(ts); break; case "pause": this.PlaybackSpeed = 0; break; case "resume": this.PlaybackSpeed = 1; break; } break; } break; } } if (TcpClientLocal != null) { TcpClientLocal.Close(); } } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message + "\n" + ex.StackTrace); if (TcpClientLocal != null) { TcpClientLocal.Close(); } } }
/// <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; } } } }
private void HandleTcpServer() { try { if (TcpClientServer == null) { return; } NetworkStream nstream = TcpClientServer.GetStream(); int connection = 0; bool changeCharacterList = false, sentAnimatedTextPacket = false; while (true) { Objects.Packet p = Objects.Packet.GetNextPacket(nstream); if (p.Length == 0) { break; } if (connection != 8) { connection = Memory.ReadByte(Addresses.Client.Connection); } if (connection == 3) { changeCharacterList = true; Memory.WriteByte(Addresses.Charlist.NumberOfCharacters, 0); } else if (connection > 5 && this.IsRunning) { if (connection == 8 && !sentAnimatedTextPacket) { Objects.Packet animPacket = new Objects.Packet(); animPacket.AddByte((byte)Addresses.Enums.IncomingPacketTypes.AnimatedText); animPacket.AddUInt16(Client.Player.X); animPacket.AddUInt16(Client.Player.Y); animPacket.AddByte(Client.Player.Z); animPacket.AddByte(181); animPacket.AddString("Recording!"); animPacket.AddLength(); if (Client.TibiaVersion >= 770) { animPacket.XteaEncrypt(this.XteaKey); animPacket.AddLength(); } animPacket.Send(TcpClientLocal); sentAnimatedTextPacket = true; } if (!this.RecordingStopwatch.IsRunning) { this.RecordingStopwatch.Start(); } if (this.CurrentRecording.MouseRecording != null && !this.CurrentRecording.MouseRecording.IsRecording) { this.CurrentRecording.MouseRecording.StartRecording(); } Objects.Packet temp = new Objects.Packet(); //temp.AddUInt32((uint)this.RecordingStopwatch.ElapsedMilliseconds); if (Client.TibiaVersion >= 770) { Objects.Packet decryptedPacket = p.XteaDecrypt(this.XteaKey, 2); //temp.AddUInt32((uint)decryptedPacket.Length); temp.AddBytes(decryptedPacket.ToBytes()); } else { //temp.AddUInt32((uint)p.Length); temp.AddBytes(p.ToBytes()); } this.CurrentRecording.Packets.Add(new Recording.Packet((uint)this.RecordingStopwatch.ElapsedMilliseconds, temp.ToBytes())); this.CurrentRecording.TimePassed = (uint)this.RecordingStopwatch.ElapsedMilliseconds; } p.Send(this.TcpClientLocal); } if (changeCharacterList) { changeCharacterList = false; for (int i = 0; i < 100; i++) { if (Memory.ReadInt(Addresses.Client.DialogOpen) > 0 && Memory.ReadByte(Addresses.Charlist.NumberOfCharacters) > 0) { this.CachedCharacterList = CharacterList.GetPlayers(); Client.Charlist.WriteIP("127.0.0.1", this.ListenerPort); break; } else if (Memory.ReadInt(Addresses.Client.DialogOpen) > 0 && Client.Misc.DialogTitle == "Enter Game") { break; } Thread.Sleep(100); } } if (this.TcpClientLocal != null) { this.TcpClientLocal.Close(); } if (this.TcpClientServer != null) { this.TcpClientServer.Close(); } } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message + "\n" + ex.StackTrace); } }