From b170c73f922e3457b923b4e7fcbec794a8885c77 Mon Sep 17 00:00:00 2001 From: Ralf Ertzinger Date: Sat, 22 Jun 2013 10:08:23 +0100 Subject: Updated IAP commands. Originally written and uploaded by Lalufu (Ralf Ertzinger) in Feb 2012. They have been condensed into a single patch and some further additions by Andy Potter. Currently includes Authentication V2 support from iPod to Accessory, RF/BlueTooth transmitter support, selecting a playlist and selecting a track from the current playlist. Does not support uploading Album Art or podcasts. Has been tested on the following iPods, 4th Gen Grayscale, 4th Gen Color/Photo, Mini 2nd Gen, Nano 1st Gen and Video 5.5Gen. Change-Id: Ie8fc098361844132f0228ecbe3c48da948726f5e Co-Authored by: Andy Potter Reviewed-on: http://gerrit.rockbox.org/533 Reviewed-by: Frank Gevaerts --- tools/iap/iap-verbose.pl | 1856 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1856 insertions(+) create mode 100644 tools/iap/iap-verbose.pl (limited to 'tools/iap/iap-verbose.pl') diff --git a/tools/iap/iap-verbose.pl b/tools/iap/iap-verbose.pl new file mode 100644 index 0000000000..ed25de7548 --- /dev/null +++ b/tools/iap/iap-verbose.pl @@ -0,0 +1,1856 @@ +#!/usr/bin/perl -w + +package iap::decode; + +use Device::iPod; +use Data::Dumper; +use strict; + +sub new { + my $class = shift; + my $self = {-state => {}}; + + return bless($self, $class); +} + +sub display { + my $self = shift; + my $lingo = shift; + my $command = shift; + my $data = shift; + my $name; + my $handler; + my $r; + + + $name = sprintf("_h_%02x_%04x", $lingo, $command); + $handler = $self->can($name); + if ($handler) { + unless(exists($self->{-state}->{$name})) { + $self->{-state}->{$name} = {}; + } + $r = $handler->($self, $data, $self->{-state}->{$name}); + } else { + $r = $self->generic($lingo, $command, $data); + } + + printf("\n"); + return $r; +} + +sub generic { + my $self = shift; + my $lingo = shift; + my $command = shift; + my $data = shift; + + printf("Unknown command\n"); + printf(" Lingo: 0x%02x\n", $lingo); + printf(" Command 0x%04x\n", $command); + printf(" Data: %s\n", Device::iPod->_hexstring($data)); + + exit(1); + + return 1; +} + +sub _h_00_0001 { + my $self = shift; + my $data = shift; + my $state = shift; + my $lingo = shift; + + $lingo = unpack("C", $data); + + printf("Identify (0x00, 0x01) D->I\n"); + printf(" Lingo: 0x%02x\n", $lingo); + + return 1; +} + +sub _h_00_0002 { + my $self = shift; + my $data = shift; + my $state = shift; + my $res; + my $cmd; + my $delay; + + ($res, $cmd, $delay) = unpack("CCN", $data); + + printf("ACK (0x00, 0x02) I->D\n"); + printf(" Acknowledged command: 0x%02x\n", $cmd); + printf(" Status: %s (%d)\n", + ("Success", + "ERROR: Unknown Database Category", + "ERROR: Command Failed", + "ERROR: Out Of Resource", + "ERROR: Bad Parameter", + "ERROR: Unknown ID", + "Command Pending", + "ERROR: Not Authenticated", + "ERROR: Bad Authentication Version", + "ERROR: Accessory Power Mode Request Failed", + "ERROR: Certificate Invalid", + "ERROR: Certificate permissions invalid", + "ERROR: File is in use", + "ERROR: Invalid file handle", + "ERROR: Directory not empty", + "ERROR: Operation timed out", + "ERROR: Command unavailable in this iPod mode", + "ERROR: Invalid accessory resistor ID", + "Reserved", + "Reserved", + "Reserved", + "ERROR: Maximum number of accessory connections already reached")[$res], $res); + if ($res == 6) { + $delay = unpack("xxN", $data); + printf(" Delay: %d ms\n", $delay); + } + + return 1; +} + +sub _h_00_0005 { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("EnterRemoteUIMode (0x00, 0x05) D->I\n"); + + return 1; +} + +sub _h_00_000d { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("RequestiPodModelNum (0x00, 0x0D) D->I\n"); + + return 1; +} + +sub _h_00_000e { + my $self = shift; + my $data = shift; + my $state = shift; + my ($modelnum, $name); + + ($modelnum, $name) = unpack("NZ*", $data); + + printf("ReturniPodModelNum (0x00, 0x0E) I->D\n"); + printf(" Model number: %08x\n", $modelnum); + printf(" Model name: %s\n", $name); + + return 1; +} + +sub _h_00_000f { + my $self = shift; + my $data = shift; + my $state = shift; + my $lingo; + + $lingo = unpack("C", $data); + + printf("RequestLingoProtocolVersion (0x00, 0x0F) D->I\n"); + printf(" Lingo: 0x%02x\n", $lingo); + + return 1; +} + +sub _h_00_0010 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($lingo, $maj, $min); + + ($lingo, $maj, $min) = unpack("CCC", $data); + + printf("ReturnLingoProtocolVersion (0x00, 0x10) I->D\n"); + printf(" Lingo: 0x%02x\n", $lingo); + printf(" Version: %d.%02d\n", $maj, $min); + + return 1; +} + + +sub _h_00_0013 { + my $self = shift; + my $data = shift; + my $state = shift; + my @lingolist; + my ($lingoes, $options, $devid); + + ($lingoes, $options, $devid) = unpack("N3", $data); + + foreach (0..31) { + push(@lingolist, $_) if ($lingoes & (1 << $_)); + } + + printf("IdentifyDeviceLingoes (0x00, 0x13) D->I\n"); + printf(" Supported lingoes: %s\n", join(", ", @lingolist)); + printf(" Options:\n"); + printf(" Authentication: %s\n", ("None", "Defer (1.0)", "Immediate (2.0)", "Reserved")[$options & 0x03]); + printf(" Power: %s\n", ("Low", "Intermittent high", "Reserved", "Constant high")[($options & 0x0C) >> 2]); + printf(" Device ID: 0x%08x\n", $devid); + + delete($self->{-state}->{'_h_00_0015'}); + + return 1; +} + +sub _h_00_0014 { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("GetDevAuthenticationInfo (0x00, 0x14) I->D\n"); + + return 1; +} + +sub _h_00_0015 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($amaj, $amin, $curidx, $maxidx, $certdata); + + $state->{-curidx} = -1 unless(exists($state->{-curidx})); + $state->{-maxidx} = -1 unless(exists($state->{-maxidx})); + $state->{-cert} = '' unless(exists($state->{-cert})); + + ($amaj, $amin, $curidx, $maxidx, $certdata) = unpack("CCCCa*", $data); + + printf("RetDevAuthenticationInfo (0x00, 0x15) D->I\n"); + printf(" Authentication version: %d.%d\n", $amaj, $amin); + printf(" Segment: %d of %d\n", $curidx, $maxidx); + + if ($curidx-1 != $state->{-curidx}) { + printf(" WARNING! Out of order segment\n"); + return 0; + } + + if (($maxidx != $state->{-maxidx}) && ($state->{-maxidx} != -1)) { + printf(" WARNING! maxidx changed midstream\n"); + return 0; + } + + if ($curidx > $maxidx) { + printf(" WARNING! Too many segments\n"); + return 0; + } + + $state->{-curidx} = $curidx; + $state->{-maxidx} = $maxidx; + $state->{-cert} .= $certdata; + + if ($curidx == $maxidx) { + printf(" Certificate: %s\n", Device::iPod->_hexstring($state->{-cert})); + } + + return 1; +} + +sub _h_00_0016 { + my $self = shift; + my $data = shift; + my $state = shift; + my $res; + + $res = unpack("C", $data); + + printf("AckDevAuthenticationInfo (0x00, 0x16) I->D\n"); + printf(" Result: "); + if ($res == 0x00) { + printf("Authentication information supported\n"); + } elsif ($res == 0x08) { + printf("Authentication information unpported\n"); + } elsif ($res == 0x0A) { + printf("Certificate invalid\n"); + } elsif ($res == 0x0B) { + printf("Certificate permissions are invalid\n"); + } else { + printf("Unknown result 0x%02x\n", $res); + } + + return 1; +} + +sub _h_00_0017 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($challenge, $retry); + + printf("GetDevAuthenticationSignature (0x00, 0x17) I->D\n"); + + if (length($data) == 17) { + ($challenge, $retry) = unpack("a16C", $data); + } elsif (length($data) == 21) { + ($challenge, $retry) = unpack("a20C", $data); + } else { + printf(" WARNING! Unsupported data length: %d\n", length($data)); + return 0; + } + + printf(" Challenge: %s (%d bytes)\n", Device::iPod->_hexstring($challenge), length($challenge)); + printf(" Retry counter: %d\n", $retry); + + return 1; +} + +sub _h_00_0018 { + my $self = shift; + my $data = shift; + my $state = shift; + my $reply; + + printf("RetDevAuthenticationSignature (0x00, 0x18) D->I\n"); + printf(" Data: %s\n", Device::iPod->_hexstring($data)); + + return 1; +} + +sub _h_00_0019 { + my $self = shift; + my $data = shift; + my $state = shift; + my $res; + + $res = unpack("C", $data); + + printf("AckiPodAuthenticationInfo (0x00, 0x19) I->D\n"); + printf(" Status: %s (%d)\n", ( + "OK")[$res], $res); + + return 1; +} + +sub _h_00_0024 { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("GetiPodOptions (0x00, 0x24) D->I\n"); + + return 1; +} + +sub _h_00_0025 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($ophi, $oplo); + + ($ophi, $oplo) = unpack("NN", $data); + + printf("RetiPodOptions (0x00, 0x25) I->D\n"); + printf(" Options:\n"); + printf(" iPod supports SetiPodPreferences\n") if ($oplo & 0x02); + printf(" iPod supports video\n") if ($oplo & 0x01); + + return 1; +} + + +sub _h_00_0027 { + my $self = shift; + my $data = shift; + my $state = shift; + my $acctype; + my $accparam; + + $acctype = unpack("C", $data); + + printf("GetAccessoryInfo (0x00, 0x27) I->D\n"); + printf(" Accessory Info Type: %s (%d)\n", ( + "Info capabilities", + "Name", + "Minimum supported iPod firmware version", + "Minimum supported lingo version", + "Firmware version", + "Hardware version", + "Manufacturer", + "Model Number", + "Serial Number", + "Maximum payload size")[$acctype], $acctype); + if ($acctype == 0x02) { + my ($modelid, $maj, $min, $rev); + + ($modelid, $maj, $min, $rev) = unpack("xNCCC", $data); + printf(" Model ID: 0x%04x\n", $modelid); + printf(" iPod Firmware: %d.%d.%d\n", $maj, $min, $rev); + } elsif ($acctype == 0x03) { + my $lingo; + + $lingo = unpack("xC", $data); + printf(" Lingo: 0x%02x\n", $lingo); + } + + return 1; +} + +sub _h_00_0028 { + my $self = shift; + my $data = shift; + my $state = shift; + my $acctype; + my $accparam; + + $acctype = unpack("C", $data); + + printf("RetAccessoryInfo (0x00, 0x28) D->I\n"); + printf(" Accessory Info Type: %s (%d)\n", ( + "Info capabilities", + "Name", + "Minimum supported iPod firmware version", + "Minimum supported lingo version", + "Firmware version", + "Hardware version", + "Manufacturer", + "Model Number", + "Serial Number", + "Maximum payload size")[$acctype], $acctype); + + if ($acctype == 0x00) { + $accparam = unpack("xN", $data); + printf(" Accessory Info capabilities\n") if ($accparam & 0x01); + printf(" Accessory name\n") if ($accparam & 0x02); + printf(" Accessory minimum supported iPod firmware\n") if ($accparam & 0x04); + printf(" Accessory minimum supported lingo version\n") if ($accparam & 0x08); + printf(" Accessory firmware version\n") if ($accparam & 0x10); + printf(" Accessory hardware version\n") if ($accparam & 0x20); + printf(" Accessory manufacturer\n") if ($accparam & 0x40); + printf(" Accessory model number\n") if ($accparam & 0x80); + printf(" Accessory serial number\n") if ($accparam & 0x100); + printf(" Accessory incoming max packet size\n") if ($accparam & 0x200); + } + + if ($acctype ~~ [0x01, 0x06, 0x07, 0x08]) { + $accparam = unpack("xZ*", $data); + printf(" Data: %s\n", $accparam); + } + + if ($acctype == 0x02) { + $accparam = [ unpack("xNCCC", $data) ]; + printf(" Model ID: %08x\n", $accparam->[0]); + printf(" Firmware version: %d.%02d.%02d\n", $accparam->[1], $accparam->[2], $accparam->[3]); + } + + if ($acctype == 0x03) { + $accparam = [ unpack("xCCC", $data) ]; + printf(" Lingo: %02x\n", $accparam->[0]); + printf(" Version: %d.%02d\n", $accparam->[1], $accparam->[2]); + } + + if ($acctype ~~ [0x04, 0x05]) { + $accparam = [ unpack("xCCC", $data) ]; + printf(" Version: %d.%02d.%02d\n", @{$accparam}); + } + + return 1; +} + +sub _h_00_0029 { + my $self = shift; + my $data = shift; + my $state = shift; + my $class; + + $class = unpack("C", $data); + + printf("GetiPodPreferences (0x00, 0x29) D->I\n"); + printf(" Class: %s (%d)\n", ( + "Video out setting", + "Screen configuration", + "Video signal format", + "Line Out usage", + "(Reserved)", + "(Reserved)", + "(Reserved)", + "(Reserved)", + "Video out connection", + "Closed captioning", + "Video aspect ratio", + "(Reserved)", + "Subtitles", + "Video alternate audio channel")[$class], $class); + + return 1; +} + +sub _h_00_002b { + my $self = shift; + my $data = shift; + my $state = shift; + my ($class, $setting); + + ($class, $setting) = unpack("CC", $data); + + printf("SetiPodPreferences (0x00, 0x2B) D->I\n"); + printf(" Class: %s (%d)\n", ( + "Video out setting", + "Screen configuration", + "Video signal format", + "Line out usage", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Video-out connection", + "Closed captioning", + "Video monitor aspect ratio", + "Reserved", + "Subtitles", + "Video alternate audio channel")[$class], $class); + printf(" Setting: %s (%d)\n", ( + ["Off", + "On", + "Ask user"], + ["Fill screen", + "Fit to screen edge"], + ["NTSC", + "PAL"], + ["Not used", + "Used"], + [], + [], + [], + [], + ["None", + "Composite", + "S-video", + "Component"], + ["Off", + "On"], + ["4:3", + "16:9"], + [], + ["Off", + "On"], + ["Off", + "On"])[$class][$setting], $setting); + + return 1; +} + +sub _h_00_0038 { + my $self = shift; + my $data = shift; + my $state = shift; + my $transid; + + $transid = unpack("n", $data); + + printf("StartIDPS (0x00, 0x38) D->I\n"); + printf(" TransID: %d\n", $transid); + + return 1; +} + +sub _h_00_003b { + my $self = shift; + my $data = shift; + my $state = shift; + my ($transid, $status); + + ($transid, $status) = unpack("nC", $data); + + printf("EndIDPS (0x00, 0x3B) D->I\n"); + printf(" TransID: %d\n", $transid); + printf(" Action: %s (%d)\n", ( + "Finished", + "Reset")[$status], $status); + + return 1; +} + +sub _h_00_004b { + my $self = shift; + my $data = shift; + my $state = shift; + my $lingo; + + $lingo = unpack("C", $data); + + printf("GetiPodOptionsForLingo (0x00, 0x4B) D->I\n"); + printf(" Lingo: 0x%02x\n", $lingo); + + return 1; +} + +sub _h_02_0000 { + my $self = shift; + my $data = shift; + my $state = shift; + my @keys; + + @keys = unpack("CCCC", $data); + + printf("ContextButtonStatus (0x02, 0x00) D->I\n"); + printf(" Buttons:\n"); + printf(" Play/Pause\n") if ($keys[0] & 0x01); + printf(" Volume Up\n") if ($keys[0] & 0x02); + printf(" Volume Down\n") if ($keys[0] & 0x04); + printf(" Next Track\n") if ($keys[0] & 0x08); + printf(" Previous Track\n") if ($keys[0] & 0x10); + printf(" Next Album\n") if ($keys[0] & 0x20); + printf(" Previous Album\n") if ($keys[0] & 0x40); + printf(" Stop\n") if ($keys[0] & 0x80); + + if (exists($keys[1])) { + printf(" Play/Resume\n") if ($keys[1] & 0x01); + printf(" Pause\n") if ($keys[1] & 0x02); + printf(" Mute toggle\n") if ($keys[1] & 0x04); + printf(" Next Chapter\n") if ($keys[1] & 0x08); + printf(" Previous Chapter\n") if ($keys[1] & 0x10); + printf(" Next Playlist\n") if ($keys[1] & 0x20); + printf(" Previous Playlist\n") if ($keys[1] & 0x40); + printf(" Shuffle Setting Advance\n") if ($keys[1] & 0x80); + } + + if (exists($keys[2])) { + printf(" Repeat Setting Advance\n") if ($keys[2] & 0x01); + printf(" Power On\n") if ($keys[2] & 0x02); + printf(" Power Off\n") if ($keys[2] & 0x04); + printf(" Backlight for 30 seconds\n") if ($keys[2] & 0x08); + printf(" Begin FF\n") if ($keys[2] & 0x10); + printf(" Begin REW\n") if ($keys[2] & 0x20); + printf(" Menu\n") if ($keys[2] & 0x40); + printf(" Select\n") if ($keys[2] & 0x80); + } + + if (exists($keys[3])) { + printf(" Up Arrow\n") if ($keys[3] & 0x01); + printf(" Down Arrow\n") if ($keys[3] & 0x02); + printf(" Backlight off\n") if ($keys[3] & 0x04); + } + + return 1; +} + +sub _h_02_0001 { + my $self = shift; + my $data = shift; + my $state = shift; + my $res; + my $cmd; + + ($res, $cmd) = unpack("CC", $data); + + printf("ACK (0x02, 0x01) I->D\n"); + printf(" Acknowledged command: 0x%02x\n", $cmd); + printf(" Status: %s (%d)\n", + ("Success", + "ERROR: Unknown Database Category", + "ERROR: Command Failed", + "ERROR: Out Of Resource", + "ERROR: Bad Parameter", + "ERROR: Unknown ID", + "Command Pending", + "ERROR: Not Authenticated", + "ERROR: Bad Authentication Version", + "ERROR: Accessory Power Mode Request Failed", + "ERROR: Certificate Invalid", + "ERROR: Certificate permissions invalid", + "ERROR: File is in use", + "ERROR: Invalid file handle", + "ERROR: Directory not empty", + "ERROR: Operation timed out", + "ERROR: Command unavailable in this iPod mode", + "ERROR: Invalid accessory resistor ID", + "Reserved", + "Reserved", + "Reserved", + "ERROR: Maximum number of accessory connections already reached")[$res], $res); + + return 1; +} + +sub _h_03_0000 { + my $self = shift; + my $data = shift; + my $state = shift; + my $res; + my $cmd; + + ($res, $cmd) = unpack("CC", $data); + + printf("ACK (0x03, 0x00) I->D\n"); + printf(" Acknowledged command: 0x%02x\n", $cmd); + printf(" Status: %s (%d)\n", + ("Success", + "ERROR: Unknown Database Category", + "ERROR: Command Failed", + "ERROR: Out Of Resource", + "ERROR: Bad Parameter", + "ERROR: Unknown ID", + "Command Pending", + "ERROR: Not Authenticated", + "ERROR: Bad Authentication Version", + "ERROR: Accessory Power Mode Request Failed", + "ERROR: Certificate Invalid", + "ERROR: Certificate permissions invalid", + "ERROR: File is in use", + "ERROR: Invalid file handle", + "ERROR: Directory not empty", + "ERROR: Operation timed out", + "ERROR: Command unavailable in this iPod mode", + "ERROR: Invalid accessory resistor ID", + "Reserved", + "Reserved", + "Reserved", + "ERROR: Maximum number of accessory connections already reached")[$res], $res); + + return 1; +} + +sub _h_03_0008 { + my $self = shift; + my $data = shift; + my $state = shift; + my $events;; + + $events = unpack("N", $data); + + printf("SetRemoteEventsNotification (0x03, 0x08) D->I\n"); + printf(" Events:\n"); + printf(" Track position in ms\n") if ($events & 0x00000001); + printf(" Track playback index\n") if ($events & 0x00000002); + printf(" Chapter index\n") if ($events & 0x00000004); + printf(" Play status\n") if ($events & 0x00000008); + printf(" Mute/UI volume\n") if ($events & 0x00000010); + printf(" Power/Battery\n") if ($events & 0x00000020); + printf(" Equalizer setting\n") if ($events & 0x00000040); + printf(" Shuffle setting\n") if ($events & 0x00000080); + printf(" Repeat setting\n") if ($events & 0x00000100); + printf(" Date and time setting\n") if ($events & 0x00000200); + printf(" Alarm setting\n") if ($events & 0x00000400); + printf(" Backlight state\n") if ($events & 0x00000800); + printf(" Hold switch state\n") if ($events & 0x00001000); + printf(" Sound check state\n") if ($events & 0x00002000); + printf(" Audiobook speed\n") if ($events & 0x00004000); + printf(" Track position in s\n") if ($events & 0x00008000); + printf(" Mute/UI/Absolute volume\n") if ($events & 0x00010000); + + return 1; +} + +sub _h_03_0009 { + my $self = shift; + my $data = shift; + my $state = shift; + my $event; + my $eventdata; + + $event = unpack("C", $data); + + printf("RemoteEventNotification (0x03, 0x09) I->D\n"); + printf(" Event: %s (%d)\n", ( + "Track position in ms", + "Track playback index", + "Chapter index", + "Play status", + "Mute/UI volume", + "Power/Battery", + "Equalizer setting", + "Shuffle setting", + "Repeat setting", + "Date and time setting", + "Alarm setting", + "Backlight state", + "Hold switch state", + "Sound check state", + "Audiobook speed", + "Track position in s", + "Mute/UI/Absolute volume")[$event], $event); + + if ($event == 0x00) { + $eventdata = unpack("xN", $data); + printf(" Position: %d ms\n", $eventdata); + } elsif ($event == 0x01) { + $eventdata = unpack("xN", $data); + printf(" Track: %d\n", $eventdata); + } elsif ($event == 0x02) { + $eventdata = [ unpack("xNnn", $data) ]; + printf(" Track: %d\n", $eventdata->[0]); + printf(" Chapter count: %d\n", $eventdata->[1]); + printf(" Chapter index: %d\n", $eventdata->[2]); + } elsif ($event == 0x03) { + $eventdata = unpack("xC", $data); + printf(" Status: %s (%d)\n", ( + "Stopped", + "Playing", + "Paused", + "FF", + "REW", + "End FF/REW")[$eventdata], $eventdata); + } elsif ($event == 0x04) { + $eventdata = [ unpack("xCC") ]; + printf(" Mute: %s\n", $eventdata->[0]?"Off":"On"); + printf(" Volume: %d\n", $eventdata->[1]); + } elsif ($event == 0x0F) { + $eventdata = unpack("xn", $data); + printf(" Position: %d s\n", $eventdata); + } + + return 1; +} + +sub _h_03_000c { + my $self = shift; + my $data = shift; + my $state = shift; + my $info; + + $info = unpack("C", $data); + + printf("GetiPodStateInfo (0x03, 0x0C) D->I\n"); + printf(" Info to get: %s (%d)\n", ( + "Track time in ms", + "Track playback index", + "Chapter information", + "Play status", + "Mute and volume information", + "Power and battery status", + "Equalizer setting", + "Shuffle setting", + "Repeat setting", + "Date and time", + "Alarm state and time", + "Backlight state", + "Hold switch state", + "Audiobook speed", + "Track time in seconds", + "Mute/UI/Absolute volume")[$info], $info); + + return 1; +} + +sub _h_03_000d { + my $self = shift; + my $data = shift; + my $state = shift; + my $type; + my $info; + + $type = unpack("C", $data); + + printf("RetiPodStateInfo (0x03, 0x0E) D->I\n"); + + if ($type == 0x00) { + $info = unpack("xN", $data); + printf(" Type: Track position\n"); + printf(" Position: %d ms\n", $info); + } elsif ($type == 0x01) { + $info = unpack("xN", $data); + printf(" Type: Track index\n"); + printf(" Index: %d\n", $info); + } elsif ($type == 0x02) { + $info = [ unpack("xNnn", $data) ]; + printf(" Type: Chapter Information\n"); + printf(" Playing Track: %d\n", $info->[0]); + printf(" Chapter count: %d\n", $info->[1]); + printf(" Chapter index: %d\n", $info->[2]); + } elsif ($type == 0x03) { + $info = unpack("xC", $data); + printf(" Type: Play status\n"); + printf(" Status: %s (%d)\n", ( + "Stopped", + "Playing", + "Paused", + "FF", + "REW", + "End FF/REW")[$info], $info); + } elsif ($type == 0x04) { + $info = [unpack("xCC", $data)]; + printf(" Type: Mute/Volume\n"); + printf(" Mute State: %s\n", $info->[0]?"On":"Off"); + printf(" Volume level: %d\n", $info->[1]); + } elsif ($type == 0x05) { + $info = [unpack("xCC", $data)]; + printf(" Type: Battery Information\n"); + printf(" Power state: %s (%d)\n", ( + "Internal, low power (<30%)", + "Internal", + "External battery pack, no charging", + "External power, no charging", + "External power, charging", + "External power, charged")[$info->[0]], $info->[0]); + printf(" Battery level: %d%%\n", $info->[1]*100/255); + } elsif ($type == 0x06) { + $info = [unpack("xN", $data)]; + printf(" Type: Equalizer\n"); + printf(" Index: %d\n", $info->[0]); + } elsif ($type == 0x07) { + $info = [unpack("xC", $data)]; + printf(" Type: Shuffle\n"); + printf(" Shuffle State: %s\n", $info->[0]?"On":"Off"); + } elsif ($type == 0x08) { + $info = [unpack("xC", $data)]; + printf(" Type: Repeat\n"); + printf(" Repeat State: %s\n", $info->[0]?"On":"Off"); + } elsif ($type == 0x09) { + $info = [unpack("xnCCCC", $data)]; + printf(" Type: Date\n"); + printf(" Date: %02d.%02d.%04d %02d:%02d\n", $info->[2], $info->[1], $info->[0], $info->[3], $info->[4]); + } elsif ($type == 0x0A) { + $info = [unpack("xCCC", $data)]; + printf(" Type: Alarm\n"); + printf(" Alarm State: %s\n", $info->[0]?"On":"Off"); + printf(" Time: %02d:%02d\n", $info->[1], $info->[2]); + } elsif ($type == 0x0B) { + $info = [unpack("xC", $data)]; + printf(" Type: Backlight\n"); + printf(" Backlight State: %s\n", $info->[0]?"On":"Off"); + } elsif ($type == 0x0C) { + $info = [unpack("xC", $data)]; + printf(" Type: Hold switch\n"); + printf(" Switch State: %s\n", $info->[0]?"On":"Off"); + } elsif ($type == 0x0D) { + $info = [unpack("xC", $data)]; + printf(" Type: Sound check\n"); + printf(" Sound check: %s\n", $info->[0]?"On":"Off"); + } elsif ($type == 0x0E) { + $info = [unpack("xC", $data)]; + printf(" Type: Audiobook speed\n"); + printf(" Speed: %s\n", $info->[0]==0x00?"Normal":$info->[0]==0x01?"Faster":$info->[0]==0xFF?"Slower":"Reserved"); + } elsif ($type == 0x0F) { + $info = unpack("xN", $data); + printf(" Type: Track position\n"); + printf(" Position: %d s\n", $info); + } elsif ($type == 0x10) { + $info = [unpack("xCCC", $data)]; + printf(" Type: Mute/UI/Absolute volume\n"); + printf(" Mute State: %s\n", $info->[0]?"On":"Off"); + printf(" UI Volume level: %d\n", $info->[1]); + printf(" Absolute Volume: %d\n", $info->[2]); + } else { + printf(" Reserved\n"); + } + + return 1; +} + + +sub _h_03_000e { + my $self = shift; + my $data = shift; + my $state = shift; + my $type; + my $info; + + $type = unpack("C", $data); + + printf("SetiPodStateInfo (0x03, 0x0E) D->I\n"); + + if ($type == 0x00) { + $info = unpack("xN", $data); + printf(" Type: Track position\n"); + printf(" Position: %d ms\n", $info); + } elsif ($type == 0x01) { + $info = unpack("xN", $data); + printf(" Type: Track index\n"); + printf(" Index: %d\n", $info); + } elsif ($type == 0x02) { + $info = unpack("xn", $data); + printf(" Type: Chapter index\n"); + printf(" Index: %d\n", $info); + } elsif ($type == 0x03) { + $info = unpack("xC", $data); + printf(" Type: Play status\n"); + printf(" Status: %s (%d)\n", ( + "Stopped", + "Playing", + "Paused", + "FF", + "REW", + "End FF/REW")[$info], $info); + } elsif ($type == 0x04) { + $info = [unpack("xCCC", $data)]; + printf(" Type: Mute/Volume\n"); + printf(" Mute State: %s\n", $info->[0]?"On":"Off"); + printf(" Volume level: %d\n", $info->[1]); + printf(" Restore on exit: %s\n", $info->[2]?"Yes":"No"); + } elsif ($type == 0x06) { + $info = [unpack("xNC", $data)]; + printf(" Type: Equalizer\n"); + printf(" Index: %d\n", $info->[0]); + printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); + } elsif ($type == 0x07) { + $info = [unpack("xCC", $data)]; + printf(" Type: Shuffle\n"); + printf(" Shuffle State: %s\n", $info->[0]?"On":"Off"); + printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); + } elsif ($type == 0x08) { + $info = [unpack("xCC", $data)]; + printf(" Type: Repeat\n"); + printf(" Repeat State: %s\n", $info->[0]?"On":"Off"); + printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); + } elsif ($type == 0x09) { + $info = [unpack("xnCCCC", $data)]; + printf(" Type: Date\n"); + printf(" Date: %02d.%02d.%04d %02d:%02d\n", $info->[2], $info->[1], $info->[0], $info->[3], $info->[4]); + } elsif ($type == 0x0A) { + $info = [unpack("xCCCC", $data)]; + printf(" Type: Alarm\n"); + printf(" Alarm State: %s\n", $info->[0]?"On":"Off"); + printf(" Time: %02d:%02d\n", $info->[1], $info->[2]); + printf(" Restore on exit: %s\n", $info->[3]?"Yes":"No"); + } elsif ($type == 0x0B) { + $info = [unpack("xCC", $data)]; + printf(" Type: Backlight\n"); + printf(" Backlight State: %s\n", $info->[0]?"On":"Off"); + printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); + } elsif ($type == 0x0D) { + $info = [unpack("xCC", $data)]; + printf(" Type: Sound check\n"); + printf(" Sound check: %s\n", $info->[0]?"On":"Off"); + printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); + } elsif ($type == 0x0E) { + $info = [unpack("xCC", $data)]; + printf(" Type: Audiobook speed\n"); + printf(" Speed: %s\n", $info->[0]==0x00?"Normal":$info->[0]==0x01?"Faster":$info->[0]==0xFF?"Slower":"Reserved"); + printf(" Restore on exit: %s\n", $info->[1]?"Yes":"No"); + } elsif ($type == 0x0F) { + $info = unpack("xN", $data); + printf(" Type: Track position\n"); + printf(" Position: %d s\n", $info); + } elsif ($type == 0x10) { + $info = [unpack("xCCCC", $data)]; + printf(" Type: Mute/UI/Absolute volume\n"); + printf(" Mute State: %s\n", $info->[0]?"On":"Off"); + printf(" UI Volume level: %d\n", $info->[1]); + printf(" Absolute Volume: %d\n", $info->[2]); + printf(" Restore on exit: %s\n", $info->[3]?"Yes":"No"); + } else { + printf(" Reserved\n"); + } + + return 1; +} + +sub _h_03_000f { + my $self = shift; + my $data = shift; + my $state = shift; + + print("GetPlayStatus (0x03, 0x0F) D->I\n"); + + return 1; +} + +sub _h_03_0010 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($status, $idx, $len, $pos); + + ($status, $idx, $len, $pos) = unpack("CNNN", $data); + + printf("RetPlayStatus (0x03, 0x10) I->D\n"); + printf(" Status: %s (%d)\n", ( + "Stopped", + "Playing", + "Paused")[$status], $status); + if ($status != 0x00) { + printf(" Track index: %d\n", $idx); + printf(" Track length: %d\n", $len); + printf(" Track position: %d\n", $pos); + } + + return 1; +} + +sub _h_03_0012 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($info, $tidx, $cidx); + + ($info, $tidx, $cidx) = unpack("CNn", $data); + + printf("GetIndexedPlayingTrackInfo (0x03, 0x12) D->I\n"); + printf(" Requested info: %s (%d)\n", ( + "Track caps/info", + "Chapter time/name", + "Artist name", + "Album name", + "Genre name", + "Track title", + "Composer name", + "Lyrics", + "Artwork count")[$info], $info); + printf(" Track index: %d\n", $tidx); + printf(" Chapter index: %d\n", $cidx); + + return 1; +} + +sub _h_03_0013 { + my $self = shift; + my $data = shift; + my $state = shift; + my $info; + + $info = unpack("C", $data); + printf("RetIndexedPlayingTrackInfo (0x03, 0x13) I->D\n"); + printf(" Returned info: %s (%d)\n", ( + "Track caps/info", + "Chapter time/name", + "Artist name", + "Album name", + "Genre name", + "Track title", + "Composer name", + "Lyrics", + "Artwork count")[$info], $info); + if ($info == 0x00) { + my ($caps, $len, $chap) = unpack("xNNn", $data); + printf(" Track is audiobook\n") if ($caps & 0x01); + printf(" Track has chapters\n") if ($caps & 0x02); + printf(" Track has artwork\n") if ($caps & 0x04); + printf(" Track contains video\n") if ($caps & 0x80); + printf(" Track queued as video\n") if ($caps & 0x100); + + printf(" Track length: %d ms\n", $len); + printf(" Track chapters: %d\n", $chap); + } elsif ($info == 0x01) { + my ($len, $name) = unpack("xNZ*"); + printf(" Chapter time: %d ms\n", $len); + printf(" Chapter name: %s\n", $name); + } elsif ($info >= 0x02 && $info <= 0x06) { + my $name = unpack("xZ*", $data); + printf(" Name/Title: %s\n", $name) + } elsif ($info == 0x07) { + my ($info, $index, $data) = unpack("xCnZ*"); + printf(" Part of multiple packets\n") if ($info & 0x01); + printf(" Is last packet\n") if ($info & 0x02); + printf(" Packet index: %d\n", $index); + printf(" Data: %s\n", $data); + } elsif ($info == 0x08) { + + } + + return 1; +} + +sub _h_04_0001 { + my $self = shift; + my $data = shift; + my $state = shift; + my $res; + my $cmd; + + ($res, $cmd) = unpack("Cn", $data); + + printf("ACK (0x04, 0x0001) I->D\n"); + printf(" Acknowledged command: 0x%02x\n", $cmd); + printf(" Status: %s (%d)\n", + ("Success", + "ERROR: Unknown Database Category", + "ERROR: Command Failed", + "ERROR: Out Of Resource", + "ERROR: Bad Parameter", + "ERROR: Unknown ID", + "Reserved", + "ERROR: Not Authenticated")[$res], $res); + + return 1; +} + +sub _h_04_000c { + my $self = shift; + my $data = shift; + my $state = shift; + my ($info, $track, $chapter); + + ($info, $track, $chapter) = unpack("CNn", $data); + + printf("GetIndexedPlayingTrackInfo (0x04, 0x000C) D->I\n"); + printf(" Track: %d\n", $track); + printf(" Chapter: %d\n", $chapter); + printf(" Info requested: %s (%d)\n", ( + "Capabilities and information", + "Podcast name", + "Track release date", + "Track description", + "Track song lyrics", + "Track genre", + "Track Composer", + "Tracn Artwork count")[$info], $info); + + return 1; +} + +sub _h_04_000d { + my $self = shift; + my $data = shift; + my $state = shift; + my $info; + + $info = unpack("C", $data); + + printf("ReturnIndexedPlayingTrackInfo (0x04, 0x000D) I->D\n"); + if ($info == 0x00) { + my ($capability, $length, $chapter); + + ($capability, $length, $chapter) = unpack("xNNn", $data); + printf(" Capabilities:\n"); + printf(" Is audiobook\n") if ($capability & 0x00000001); + printf(" Has chapters\n") if ($capability & 0x00000002); + printf(" Has album artwork\n") if ($capability & 0x00000004); + printf(" Has song lyrics\n") if ($capability & 0x00000008); + printf(" Is a podcast episode\n") if ($capability & 0x00000010); + printf(" Has release date\n") if ($capability & 0x00000020); + printf(" Has description\n") if ($capability & 0x00000040); + printf(" Contains video\n") if ($capability & 0x00000080); + printf(" Queued to play as video\n") if ($capability & 0x00000100); + + printf(" Length: %d ms\n", $length); + printf(" Chapters: %d\n", $chapter); + } else { + printf(" WARNING: Unknown info\n"); + return 1; + } + + return 1; +} + +sub _h_04_0012 { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("RequestProtocolVersion (0x04, 0x0012) D->I\n"); + + return 1; +} + +sub _h_04_0013 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($maj, $min); + + ($maj, $min) = unpack("CC", $data); + + printf("ReturnProtocolVersion (0x04, 0x0013) I->D\n"); + printf(" Lingo 0x04 version: %d.%02d\n", $maj, $min); + + return 1; +} + +sub _h_04_0016 { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("ResetDBSelection (0x04, 0x0016) D->I\n"); + + return 1; +} + +sub _h_04_0018 { + my $self = shift; + my $data = shift; + my $state = shift; + my $category; + + $category = unpack("C", $data); + + printf("GetNumberCategorizedDBRecords (0x04, 0x0018) D->I\n"); + printf(" Category: %s (%d)\n", ( + "Reserved", + "Playlist", + "Artist", + "Album", + "Genre", + "Track", + "Composer", + "Audiobook", + "Podcast", + "Nested Playlist")[$category], $category); + + return 1; +} + +sub _h_04_0019 { + my $self = shift; + my $data = shift; + my $state = shift; + my $count; + + $count = unpack("N", $data); + + printf("ReturnNumberCategorizedDBRecords (0x04, 0x0019) I->D\n"); + printf(" Count: %d\n", $count); + + return 1; +} + +sub _h_04_001c { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("GetPlayStatus (0x04, 0x001C) D->I\n"); + + return 1; +} + +sub _h_04_001d { + my $self = shift; + my $data = shift; + my $state = shift; + my ($len, $pos, $s); + + ($len, $pos, $s) = unpack("NNC", $data); + + printf("ReturnPlayStatus (0x04, 0x001D) I->D\n"); + printf(" Song length: %d ms\n", $len); + printf(" Song position: %d ms\n", $pos); + printf(" Player state: "); + if ($s == 0x00) { + printf("Stopped\n"); + } elsif ($s == 0x01) { + printf("Playing\n"); + } elsif ($s == 0x02) { + printf("Paused\n"); + } elsif ($s == 0xFF) { + printf("Error\n"); + } else { + printf("Reserved\n"); + } + + return 1; +} + +sub _h_04_001e { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("GetCurrentPlayingTrackIndex (0x04, 0x001E) D->I\n"); + + return 1; +} + +sub _h_04_001f { + my $self = shift; + my $data = shift; + my $state = shift; + my $num; + + $num = unpack("N", $data); + + printf("ReturnCurrentPlayingTrackIndex (0x04, 0x001F) I->D\n"); + printf(" Index: %d\n", $num); + + return 1; +} + +sub _h_04_0020 { + my $self = shift; + my $data = shift; + my $state = shift; + my $track; + + $track = unpack("N", $data); + + printf("GetIndexedPlayingTrackTitle (0x04, 0x0020) D->I\n"); + printf(" Track: %d\n", $track); + + return 1; +} + +sub _h_04_0021 { + my $self = shift; + my $data = shift; + my $state = shift; + my $title; + + $title = unpack("Z*", $data); + + printf("ReturnIndexedPlayingTrackTitle (0x04, 0x0021) I->D\n"); + printf(" Title: %s\n", $title); + + return 1; +} + +sub _h_04_0022 { + my $self = shift; + my $data = shift; + my $state = shift; + my $track; + + $track = unpack("N", $data); + + printf("GetIndexedPlayingTrackArtistName (0x04, 0x0022) D->I\n"); + printf(" Track: %d\n", $track); + + return 1; +} + +sub _h_04_0023 { + my $self = shift; + my $data = shift; + my $state = shift; + my $artist; + + $artist = unpack("Z*", $data); + + printf("ReturnIndexedPlayingTrackArtistName (0x04, 0x0023) I->D\n"); + printf(" Artist: %s\n", $artist); + + return 1; +} + +sub _h_04_0024 { + my $self = shift; + my $data = shift; + my $state = shift; + my $track; + + $track = unpack("N", $data); + + printf("GetIndexedPlayingTrackAlbumName (0x04, 0x0024) D->I\n"); + printf(" Track: %d\n", $track); + + return 1; +} + +sub _h_04_0025 { + my $self = shift; + my $data = shift; + my $state = shift; + my $title; + + $title = unpack("Z*", $data); + + printf("ReturnIndexedPlayingTrackAlbumName (0x04, 0x0025) I->D\n"); + printf(" Album: %s\n", $title); + + return 1; +} + +sub _h_04_0026 { + my $self = shift; + my $data = shift; + my $state = shift; + my $notification; + + if (length($data) == 1) { + $notification = unpack("C", $data); + } elsif (length($data) == 4) { + $notification = unpack("N", $data); + } + + printf("SetPlayStatusChangeNotification (0x04, 0x0026) D->I\n"); + + if (length($data) == 1) { + printf(" Events for: %s (%d)\n", ( + "Disable all", + "Basic play state, track index, track time position, FFW/REW seek stop, and chapter index changes")[$notification], $notification); + } elsif (length($data) == 4) { + printf(" Events for:\n"); + printf(" Basic play state changes\n") if ($notification & 0x00000001); + printf(" Extended play state changes\n") if ($notification & 0x00000002); + printf(" Track index\n") if ($notification & 0x00000004); + printf(" Track time offset (ms)\n") if ($notification & 0x00000008); + printf(" Track time offset (s)\n") if ($notification & 0x00000010); + printf(" Chapter index\n") if ($notification & 0x00000020); + printf(" Chapter time offset (ms)\n") if ($notification & 0x00000040); + printf(" Chapter time offset (s)\n") if ($notification & 0x00000080); + printf(" Track unique identifier\n") if ($notification & 0x00000100); + printf(" Track media tyoe\n") if ($notification & 0x00000200); + printf(" Track lyrics\n") if ($notification & 0x00000400); + } else { + printf(" WARNING: Unknown length for state\n"); + return 0; + } + + return 1; +} + +sub _h_04_0027 { + my $self = shift; + my $data = shift; + my $state = shift; + my $info; + + $info = unpack("C", $data); + + printf("PlayStatusChangeNotification (0x04, 0x0029) I->D\n"); + printf(" Status:\n"); + if ($info == 0x00) { + printf(" Playback stopped\n"); + } elsif ($info == 0x01) { + my $index = unpack("xN", $data); + + printf(" Track index: %d\n", $index); + } elsif ($info == 0x02) { + printf(" Playback FF seek stop\n"); + } elsif ($info == 0x03) { + printf(" Playback REW seek stop\n"); + } elsif ($info == 0x04) { + my $offset = unpack("xN", $data); + + printf(" Track time offset: %d ms\n", $offset); + } elsif ($info == 0x05) { + my $index = unpack("xN", $data); + + printf(" Chapter index: %d\n", $index); + } elsif ($info == 0x06) { + my $status = unpack("xC", $data); + + printf(" Playback status extended: %s (%d)\n", ( + "Reserved", + "Reserved", + "Stopped", + "Reserved", + "Reserved", + "FF seek started", + "REW seek started", + "FF/REW seek stopped", + "Reserved", + "Reserved", + "Playing", + "Paused")[$status], $status); + } elsif ($info == 0x07) { + my $offset = unpack("xN", $data); + + printf(" Track time offset: %d s\n", $offset); + } elsif ($info == 0x08) { + my $offset = unpack("xN", $data); + + printf(" Chapter time offset %d ms\n", $offset); + } elsif ($info == 0x09) { + my $offset = unpack("xN", $data); + + printf(" Chapter time offset %d s\n", $offset); + } elsif ($info == 0x0A) { + my ($uidhi, $uidlo) = unpack("xNN", $data); + + printf(" Track UID: %08x%08x\n", $uidhi, $uidlo); + } elsif ($info == 0x0B) { + my $mode = unpack("xC", $data); + + printf(" Track mode: %s (%d)\n", ( + "Audio track", + "Video track")[$mode], $mode); + } elsif ($info == 0x0C) { + printf(" Track lyrics ready\n"); + } else { + printf(" Reserved\n"); + } + + return 1; +} + +sub _h_04_0029 { + my $self = shift; + my $data = shift; + my $state = shift; + my $control; + + $control = unpack("C", $data); + + printf("PlayControl (0x04, 0x0029) D->I\n"); + printf(" Command: %s (%d)\n", ( + "Reserved", + "Toggle Play/Pause", + "Stop", + "Next track", + "Previous track", + "Start FF", + "Start Rev", + "Stop FF/Rev", + "Next", + "Previous", + "Play", + "Pause", + "Next chapter", + "Previous chapter")[$control], $control); + + return 1; +} + +sub _h_04_002c { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("GetShuffle (0x04, 0x002C) D->I\n"); + + return 1; +} + +sub _h_04_002d { + my $self = shift; + my $data = shift; + my $state = shift; + my $mode; + + $mode = unpack("C", $data); + + printf("ReturnShuffle (0x04, 0x002D) I->D\n"); + printf(" Mode: %s (%d)\n", ( + "Off", + "Tracks", + "Albums")[$mode], $mode); + + return 1; +} + +sub _h_04_002f { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("GetRepeat (0x04, 0x002F) D->I\n"); + + return 1; +} + +sub _h_04_0030 { + my $self = shift; + my $data = shift; + my $state = shift; + my $mode; + + $mode = unpack("C", $data); + + printf("ReturnRepeat (0x04, 0x0030) I->D\n"); + printf(" Mode: %s (%d)\n", ( + "Off", + "One Track", + "All Tracks")[$mode], $mode); + + return 1; +} + +sub _h_04_0032 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($index, $format, $width, $height, $stride, $imagedata); + + $state->{-index} = -1 unless(exists($state->{-index})); + $state->{-image} = '' unless(exists($state->{-image})); + + ($index, $format, $width, $height, $stride, $imagedata) = unpack("nCnnNa*", $data); + + printf("SetDisplayImage (0x04, 0x0032) D->I\n"); + if ($index == 0) { + printf(" Width: %d\n", $width); + printf(" Height: %d\n", $height); + printf(" Stride: %d\n", $stride); + printf(" Format: %s (%d)\n", ( + "Reserved", + "Monochrome, 2 bps", + "RGB 565, little endian", + "RGB 565, big endian")[$format], $format); + + $state->{-imagelength} = $height * $stride; + } else { + ($index, $imagedata) = unpack("na*", $data); + } + + if ($index-1 != $state->{-index}) { + printf(" WARNING! Out of order segment\n"); + return 0; + } + + $state->{-index} = $index; + $state->{-image} .= $imagedata; + + if (length($state->{-image}) >= $state->{-imagelength}) { + printf(" Image data: %s\n", Device::iPod->_hexstring($state->{-image})); + } + + return 1; +} + +sub _h_04_0033 { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("GetMonoDisplayImageLimits (0x04, 0x0033) D->I\n"); + + return 1; +} + +sub _h_04_0034 { + my $self = shift; + my $data = shift; + my $state = shift; + my ($width, $height, $format); + + ($width, $height, $format) = unpack("nnC", $data); + + printf("ReturnMonoDisplayImageLimits (0x04, 0x0034) I->D\n"); + printf(" Width: %d\n", $width); + printf(" Height: %d\n", $height); + printf(" Format: %s (%d)\n", ( + "Reserved", + "Monochrome, 2 bps", + "RGB 565, little endian", + "RGB 565, big endian")[$format], $format); + + return 1; +} + +sub _h_04_0035 { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("GetNumPlayingTracks (0x04, 0x0035) D->I\n"); + + return 1; +} + +sub _h_04_0036 { + my $self = shift; + my $data = shift; + my $state = shift; + my $num; + + $num = unpack("N", $data); + + printf("ReturnNumPlayingTracks (0x04, 0x0036) I->D\n"); + printf(" Number: %d\n", $num); + + return 1; +} + +sub _h_04_0037 { + my $self = shift; + my $data = shift; + my $state = shift; + my $num; + + $num = unpack("N", $data); + + printf("SetCurrentPlayingTrack (0x04, 0x0037) D->I\n"); + printf(" Track: %d\n", $num); + + return 1; +} + +sub _h_07_0005 { + my $self = shift; + my $data = shift; + my $state = shift; + my $control; + + $control = unpack("C", $data); + + printf("SetTunerCtrl (0x07, 0x05) I->D\n"); + printf(" Options:\n"); + printf(" Power %s\n", ($control & 0x01)?"on":"off"); + printf(" Status change notifications %s\n", ($control & 0x02)?"on":"off"); + printf(" Raw mode %s\n", ($control & 0x04)?"on":"off"); +} + +sub _h_07_0020 { + my $self = shift; + my $data = shift; + my $state = shift; + my $options; + + $options = unpack("N", $data); + + printf("SetRDSNotifyMask (0x07, 0x20) I->D\n"); + printf(" Options:\n"); + printf(" Radiotext\n") if ($options & 0x00000010); + printf(" Program Service Name\n") if ($options & 0x40000000); + printf(" Reserved\n") if ($options & 0xBFFFFFEF); + + return 1; +} + +sub _h_07_0024 { + my $self = shift; + my $data = shift; + my $state = shift; + + printf("Reserved command (0x07, 0x24) I->D\n"); + + return 1; +} + + +package main; + +use Device::iPod; +use Getopt::Long; +use strict; + +my $decoder; +my $device; +my $unpacker; +my $line; + +sub unpack_hexstring { + my $line = shift; + my $m; + my @m; + + $line =~ s/(..)/chr(hex($1))/ge; + $device->{-inbuf} = $line; + + $m = $device->_message(); + next unless defined($m); + @m = $device->_unframe_cmd($m); + unless(@m) { + printf("Line %d: Error decoding frame: %s\n", $., $device->error()); + return (); + } + + return @m; +} + +sub unpack_iaplog { + my $line = shift; + my @m; + + unless ($line =~ /^(?:\[\d+\] )?[RT]::? /) { + printf("Skipped: %s\n", $line); + return (); + } + + $line =~ s/^(?:\[\d+\] )?[RT]::? //; + $line =~ s/\\x(..)/chr(hex($1))/ge; + $line =~ s/\\\\/\\/g; + + @m = unpack("CCa*", $line); + if ($m[0] == 0x04) { + @m = unpack("Cna*", $line); + } + + return @m; +} + + +$decoder = iap::decode->new(); +$device = Device::iPod->new(); +$unpacker = \&unpack_iaplog; + +GetOptions("hexstring" => sub {$unpacker = \&unpack_hexstring}); + +while ($line = <>) { + my @m; + + chomp($line); + + @m = $unpacker->($line); + next unless (@m); + + printf("Line %d: ", $.); + $decoder->display(@m); +} -- cgit v1.2.3