File Coverage

blib/lib/Data/Petitcom/PTC.pm
Criterion Covered Total %
statement 106 106 100.0
branch 19 26 73.0
condition 3 3 100.0
subroutine 28 28 100.0
pod 0 10 0.0
total 156 173 90.1


line stmt bran cond sub pod time code
1             package Data::Petitcom::PTC;
2              
3 8     8   45 use strict;
  8         15  
  8         323  
4 8     8   1240 use warnings;
  8         54  
  8         230  
5 8     8   16415 use bytes ();
  8         56  
  8         168  
6              
7 8     8   42 use Carp ();
  8         13  
  8         125  
8 8     8   40 use Digest::MD5;
  8         12  
  8         371  
9 8     8   46 use List::Util;
  8         16  
  8         514  
10 8     8   1879 use Data::Petitcom::Resource;
  8         16  
  8         609  
11              
12 8     8   67 use constant PTC => 'PETITCOM';
  8         70  
  8         2124  
13 8     8   53 use constant PTC_NAME_MAXLENGTH => 8;
  8         14  
  8         389  
14 8     8   44 use constant PTC_SIGNATURE => 'PX01';
  8         17  
  8         429  
15 8     8   44 use constant PTC_VERSION => [ 'PETC0100', 'PETC0300' ];
  8         12  
  8         533  
16 8         388 use constant PTC_RESOURCE => {
17             PRG => 0x00,
18             GRP => 0x02,
19             CHR => 0x03,
20             COL => 0x05,
21 8     8   43 };
  8         24  
22              
23 8     8   43 use constant PTC_OFFSET_RESOURCE => 0x08;
  8         13  
  8         362  
24 8     8   40 use constant PTC_OFFSET_NAME => 0x0C;
  8         22  
  8         355  
25 8     8   50 use constant PTC_OFFSET_VERSION => 0x24;
  8         17  
  8         396  
26 8     8   41 use constant PTC_OFFSET_DATA => 0x30;
  8         14  
  8         10455  
27              
28             my %defaults = (
29             data => '',
30             resource => 'PRG',
31             version => 'PETC0300',
32             name => 'DPTC',
33             );
34              
35             sub new {
36 18 50   18 0 2594 my $class = ref $_[0] ? ref shift : shift;
37 18         69 my $self = bless {}, $class;
38 18 50       190 $self->init(@_) if ( $self->can('init') );
39 18         67 return $self;
40             }
41              
42             sub init {
43 18     18 0 42 my $self = shift;
44 18         70 my %args = @_;
45 18         109 for ( keys %defaults ) {
46 72   100     317 my $value = $args{$_} || $defaults{$_};
47 72 50       407 ( $self->can($_) ) ? $self->$_($value) : ( $self->{$_} = $value );
48             }
49 18         55 return $self;
50             }
51              
52             sub resource {
53 111     111 0 1470 my $self = shift;
54 111 100       263 if (@_) {
55 28         57 my $resource = uc(shift);
56 28 50       140 ( defined PTC_RESOURCE->{$resource} )
57             ? $self->{resource} = $resource
58             : Carp::croak "unsupported resource: $resource";
59             }
60 111         2114 return $self->{resource};
61             }
62              
63             sub name {
64 37     37 0 88 my $self = shift;
65 37 100       119 if (@_) {
66 26         114 my $name = shift;
67 26         69 $name =~ s/\x00//g;
68 26 50       171 Carp::croak "name allows '_0-9A-Z': $name"
69             unless ( $name =~ /^[_0-9a-zA-Z]+$/ );
70 26         151 $self->{name}
71             = bytes::substr uc($name), 0, PTC_NAME_MAXLENGTH;
72             }
73 37         4864 return $self->{name};
74             }
75              
76             sub version {
77 96     96 0 382 my $self = shift;
78 96 100       294 if (@_) {
79 26         59 my $version = uc(shift);
80             $self->{version}
81 26 50   43   122 = List::Util::first { $_ eq $version } @{ PTC_VERSION() }
  43         152  
  26         169  
82             or Carp::croak "unsupported version: $version";
83             }
84 96         657 return $self->{version};
85             }
86              
87             sub data {
88 68     68 0 160 my $self = shift;
89 68 100       247 $self->{data} = shift if ( @_ > 0 );
90 68         163 my $format = $self->version . uc( 'R' . $self->resource );
91 68         1636 return $format . $self->{data};
92             }
93              
94             sub dump {
95 9     9 0 551 my $self = shift;
96 9         29 my $header .= PTC_SIGNATURE;
97 9         39 $header .= pack 'V', bytes::length( $self->data );
98 9         89 $header .= pack 'V', PTC_RESOURCE->{ $self->resource };
99 9         45 $header .= bytes::substr $self->name . "\x00" x PTC_NAME_MAXLENGTH, 0, PTC_NAME_MAXLENGTH;
100 9         91 $header .= Digest::MD5::md5(PTC . $self->data);
101 9         40 my $raw_ptc = $header . $self->data;
102 9         5006 return $raw_ptc;
103             }
104              
105             sub load {
106 6 100   6 0 4824 my $self = ref $_[0] ? shift : shift->new;
107 6         18 my $raw_ptc = shift;
108 6 50       32 Carp::croak "unsupported data:" unless ( $self->is_ptc($raw_ptc) );
109 6         75 my $r_int = unpack 'V', bytes::substr( $raw_ptc, PTC_OFFSET_RESOURCE, 4 );
110 6     19   79 $self->resource(List::Util::first { PTC_RESOURCE->{$_} == $r_int } keys %{ PTC_RESOURCE() });
  19         42  
  6         37  
111 6         33 $self->name( bytes::substr $raw_ptc, PTC_OFFSET_NAME, 8 );
112 6         21 $self->version( bytes::substr $raw_ptc, PTC_OFFSET_VERSION, 8 );
113 6         22 $self->data( bytes::substr $raw_ptc, PTC_OFFSET_DATA );
114 6         23 return $self;
115             }
116              
117             sub restore {
118 4     4 0 657 my $self = shift;
119 4         12 my $resource = get_resource( resource => $self->resource );
120 4         25 $resource->load($self, @_);
121 4         27 return $resource;
122             }
123              
124             sub is_ptc {
125 8     8 0 19 my $class = shift;
126 8 100       35 return 1 if ( bytes::substr( $_[0], 0, 4 ) eq PTC_SIGNATURE );
127             }
128              
129             1;