#!/usr/bin/perl

use IO::Socket;
use Data::Dumper;

$atem_ip   = "217.196.67.93";
$atem_port = "9910";

$sock = IO::Socket::INET->new(
		Proto => 'udp',
		PeerPort => $atem_port,
		PeerAddr => $atem_ip
)  || die "Socket fail: $@";

# build hello packet
$uid   = (((rand(254) + 1) << 8) + rand(254) + 1) & 0x7FFF;
$data  = pack("nnnn", 0x0100, 0x0000, 0x0000, 0x0000);
$hello = pack("CCnnnnna*", 0x10, 0x14, $uid, 0, 0, 0, 0, $data);
$sock->send($hello);
print rhexdump($hello, ">");

$mycnt = 0;
$hello_cnt = 0;
$hello_finished = 0;

while($sock->recv($data, 4096)) {
	print "\n";
	($cmd, $len, $uid, $cnt_out, $unkn1, $unkn2, $cnt_in, $payload) = unpack("CCnnnnna*", $data);
	($port, $ipaddr) = sockaddr_in($sock->peername);
	$len = (($cmd & 0x07) << 8) + $len;
	$cmd = $cmd & 0xF8;

	# debug
	#if ($hello_finished) {
		print rhexdump($data, "<");
	#}

	printf("CMD: %02X - LEN: %4d (%03X) - UID: %04X\n",
		$cmd, $len, $len, $uid);
	printf("CNT_OUT: %04X - CNT_IN: %04X - UNKN1: %04X - UNKN2: %04X\n",
		$cnt_out, $cnt_in, $unkn1, $unkn2);

	# this is a hello response
	# - grep our new UID from payload
	# - send an ACK (0x80)
	if ($cmd & 0x10) {
		# hello response
		(undef, $new_uid, undef, undef) = unpack("nnnn", $payload);
		send_pkt(0x80, $uid, 0, 0, 0, 0, '');
	}

	# this is an ACK REQUEST
	# - check for incoming packet counter
	# - set internal hello state
	# - send an ack
	if ($cmd & 0x08) {
		if ($hello_finished == 0 && $len < 1400) {
			$hello_finished = 1;
		}
		if ($hello_finished) {
			# ack the packet
			send_pkt(0x80, $uid, $cnt_in, 0, 0, 0, '');

			# request an ack (keepalive, not necessary)
			$mycnt++;
			send_pkt(0x08, $uid, 0, 0, 0, $mycnt, '');
		}
	}

	# now we can communicate
	if ($hello_finished == 1) {
		# parse payload
		while(length($payload) > 0) {
			$l = unpack("n", $payload);
			$pkt = substr($payload, 0, $l);
			$payload = substr($payload, $l);

			print rhexdump($pkt, "<<");
			my (undef, undef, $cmd, $data) = unpack("nnA4A*", $pkt);

			print "<< Command: $cmd / Len: $l\n";
			print rhexdump($data, "<<<");

			if ($cmd eq "TlIn") {
				my $inputs = unpack("n", $data);
				$data = substr($data, 2);
				my @ins = unpack("C*", $data);
				print "Inputs: $inputs\n";
				$program = 0; $preview = 0;
				for($i = 0; $i < $inputs; $i++) {
					printf("%d ", $ins[$i]);
					if ($ins[$i] & 0x01) {
						$program = $i + 1;
					}
					if ($ins[$i] & 0x02) {
						$preview = $i + 1;
					}
				} 
				print "\n";
				print "PRG: $program / PVW: $preview\n";
			}
		}

		# switch channel every 5 packets
		if (($mycnt % 5) == 0) {
			$channel++;
			if ($channel > 14) {
				$channel = 0;
			}

			print("Switch to preview " . $channel . "\n");
			$rpayload = '000c0000435076490000'.sprintf("%04X", $channel);
			# len 12, unkn, CPvI (0x43507649), ME 1 (0x0000), Channel (0x00xx) 

			$mycnt++;
			send_pkt(0x08, $uid, 0, 0, 0, $mycnt, pack("H*", $rpayload));
		}
	}
}

sub send_pkt {
	my $cmd     = shift;
	my $uid     = shift;
	my $cout    = shift;
	my $un1     = shift;
	my $un2     = shift;
	my $cin     = shift;
	my $payload = shift;

	my $len = 12 + length($payload);
	$cmd = $cmd + (($len >> 8) & 0x07);

	my $pkt = pack("CCnnnnna*", $cmd, $len, $uid, $cout, $un1, $un2, $cin, $payload);
	print rhexdump($pkt, ">");
	$sock->send($pkt);
}

	($cmd, $len, $uid, $cnt_out, $unkn1, $unkn2, $cnt_in, $payload) = unpack("CCnnnnna*", $data);



sub rhexdump {
	my $input  = shift;
	my $prefix = shift;

	my $ret = "";
	my $i = 0;
	my $j = 0;
	my @lhs = ();
	my @rhs = ();

	if (!$prefix) {
		$prefix = " ";
	}

	my @chars = split('', $input);
	for ($i = 0; $i <= $#chars; $i++) {
		my $token = ord($chars[$i]);
		$lhs[$j] .= sprintf(" %02X", $token);
		if ($token >= 0x20 && $token <= 0x7E) {
			$rhs[$j] .= sprintf("%c", $token);
		} else {
			$rhs[$j] .= ".";
		}
		if ((($i + 1) % 8) == 0) {
			$lhs[$j] .= " ";
			$rhs[$j] .= " ";
		}
		if ((($i + 1) % 16) == 0) {
			$lhs[$j] = substr($lhs[$j], 0, -1);
			$rhs[$j] = substr($rhs[$j], 0, -1);
			$j++;
		}
	}

	my $len = length($lhs[-1]);
	if ($len > 0 && $len != 49) {
		$lhs[-1] .= (" " x (49 - $len));
		$rhs[-1] .= (" " x (17 - ($len / 3)));
	}

	for ($i = 0; $i <= $#lhs; $i++) {
		$ret .= sprintf("%s %04X %s  |%s|\n", $prefix, ($i * 16), $lhs[$i], $rhs[$i]);
	}

	return($ret);
}

exit 0;
