File Coverage

blib/lib/D64/Disk/Layout.pm
Criterion Covered Total %
statement 89 91 97.8
branch 10 12 83.3
condition 3 6 50.0
subroutine 15 15 100.0
pod 6 6 100.0
total 123 130 94.6


line stmt bran cond sub pod time code
1             package D64::Disk::Layout;
2              
3             =head1 NAME
4              
5             D64::Disk::Layout - Handling entire Commodore (D64/D71/D81) disk image data in pure Perl
6              
7             =head1 SYNOPSIS
8              
9             use D64::Disk::Layout;
10              
11             # Create an empty disk layout instance:
12             my $layout = D64::Disk::Layout->new();
13              
14             # Read disk image layout from existing file:
15             my $layout = D64::Disk::Layout->new('image.d64');
16              
17             # Get disk sector object from a disk layout:
18             my $sector_layout = $layout->sector(track => $track, sector => $sector);
19              
20             # Put new data into specific disk layout sector:
21             $layout->sector(data => $sector_layout);
22             # Update an arbitrary disk layout sector with data:
23             $layout->sector(data => $sector_layout, track => $track, sector => $sector);
24              
25             # Fetch disk layout data as an array of 683 sector objects:
26             my @sector_layouts = $layout->sectors();
27              
28             # Update disk layout given an array of arbitrary sector objects:
29             $layout->sectors(sectors => \@sector_layouts);
30              
31             # Fetch disk layout data as a scalar of 683 * 256 bytes:
32             my $data = $layout->data();
33              
34             # Update disk layout providing 683 * 256 bytes of scalar data:
35             $layout->data(data => $data);
36              
37             # Print out nicely formatted human-readable form of a track/sector data:
38             $layout->print(fh => $fh, track => $track, sector => $sector);
39              
40             =head1 DESCRIPTION
41              
42             C provides a helper class for C module, enabling users to easily access and manipulate entire D64/D71/D81 disk image data in an object oriented way without the hassle of worrying about the meaning of individual bits and bytes located on every track and sector of a physical disk image. The whole family of C modules has been implemented in pure Perl as an alternative to Per Olofsson's "diskimage.c" library originally written in an ANSI C.
43              
44             C is completely unaware of an internal structure, configuration and meaning of individual sectors on disk. It only knows how to fetch and store bytes of data. Standard C disk image of C<170> kbytes is split into C<683> sectors on C<35> tracks, each of the sectors holding C<256> bytes. See description of the L module for a detailed description on accessing individual disk image files and preserving disk directory structure.
45              
46             =head1 METHODS
47              
48             =cut
49              
50 6     6   111435 use bytes;
  6         127  
  6         33  
51 6     6   191 use strict;
  6         13  
  6         124  
52 6     6   3587 use utf8;
  6         88  
  6         35  
53 6     6   192 use warnings;
  6         11  
  6         293  
54              
55             our $VERSION = '0.02';
56              
57 6     6   34 use base qw(D64::Disk::Layout::Base);
  6         14  
  6         3622  
58 6     6   17545 use Carp qw(carp croak);
  6         14  
  6         313  
59 6     6   3528 use D64::Disk::Layout::Sector;
  6         140477  
  6         266  
60 6     6   4042 use List::MoreUtils qw(arrayify zip6);
  6         95404  
  6         39  
61              
62             # Number of bytes per sector storage:
63             our $bytes_per_sector = 256;
64              
65             # Number of sectors per track storage:
66             our @sectors_per_track = ( 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, # tracks 1-17
67             19, 19, 19, 19, 19, 19, 19, # tracks 18-24
68             18, 18, 18, 18, 18, 18, # tracks 25-30
69             17, 17, 17, 17, 17, # tracks 31-35
70             # 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, # tracks 31-40
71             );
72              
73             =head2 new
74              
75             Create empty unformatted D64 disk image layout:
76              
77             my $d64DiskLayoutObj = D64::Disk::Layout->new();
78              
79             Read D64 disk image layout from existing file:
80              
81             my $d64DiskLayoutObj = D64::Disk::Layout->new('image.d64');
82              
83             A valid D64::Disk::Layout object is returned upon success, an undefined value otherwise.
84              
85             =cut
86              
87             sub new {
88 19     19 1 385064 my ($class) = shift ;
89 19         156 my $self = $class->SUPER::new(@_);
90 19 50       203 if (defined $self) {
91 19         68 bless $self, $class;
92 19         101 return $self;
93             }
94             else {
95 0         0 carp 'Failed to create new ' . __PACKAGE__ . ' object';
96 0         0 return undef;
97             }
98             }
99              
100             sub _initialize {
101 19     19   265 my ($self) = shift;
102 19         107 $self->SUPER::_initialize(@_);
103 19         3730697 return 1;
104             }
105              
106             =head2 sector
107              
108             Retrieve disk sector object from a disk layout:
109              
110             my $sectorObj = $d64DiskLayoutObj->sector(track => $track, sector => $sector);
111              
112             Insert new data into specific disk layout sector:
113              
114             $d64DiskLayoutObj->sector(data => $sectorObj);
115              
116             Update an arbitrary disk layout sector with data:
117              
118             $d64DiskLayoutObj->sector(data => $sectorObj, track => $track, sector => $sector);
119              
120             =cut
121              
122             sub sector {
123 4103     4103 1 18749 my ($self, %args) = @_;
124 4103         7394 my $track = $args{track};
125 4103         6262 my $sector = $args{sector};
126 4103         6008 my $data = $args{data};
127 4103 100       9817 if (defined $data) {
128 2 100 66     14 unless (defined $track && defined $sector) {
129 1         4 $track = $data->track();
130 1         13 $sector = $data->sector();
131             }
132 2         18 $self->sector_data($track, $sector, $data->data());
133             }
134 4103         12937 my @data = $self->sector_data($track, $sector);
135 4103         1321221 return D64::Disk::Layout::Sector->new(data => \@data, track => $track, sector => $sector);
136             }
137              
138             =head2 sectors
139              
140             Fetch disk layout data as a flattened array of 683 sector objects:
141              
142             my @sector_layouts = $d64DiskLayoutObj->sectors();
143              
144             Update disk layout given an array of arbitrary sector objects:
145              
146             $d64DiskLayoutObj->sectors(sectors => \@sector_layouts);
147              
148             =cut
149              
150             sub sectors {
151 5     5 1 57715 my ($self, %args) = @_;
152 5         37 my $sectors = $args{sectors};
153 5 100       26 if (defined $sectors) {
154 2         7 for my $sectorObj (@{$sectors}) {
  2         9  
155 704         209041 my $track = $sectorObj->track();
156 704         9257 my $sector = $sectorObj->sector();
157 704         8168 my $data = $sectorObj->data();
158 704         32838 $self->sector_data($track, $sector, $data);
159             }
160             }
161 5         603 my @sector_layouts = arrayify $self->tracks();
162 5         123330 return @sector_layouts;
163             }
164              
165             =head2 tracks
166              
167             Fetch disk layout data as an array of 35 arrays of sector objects allocated by their respective track numbers:
168              
169             my @track_layouts = $d64DiskLayoutObj->tracks();
170              
171             =cut
172              
173             sub tracks {
174 6     6 1 24 my ($self) = @_;
175 6         90 my @track_numbers = (1 .. @sectors_per_track);
176             my @track_layouts = map {
177 6         162 my ($num_sectors, $track) = @{$_};
  210         610004  
  210         727  
178             [
179             map {
180 210         831 my $sector = $_;
  4098         11640056  
181 4098         10965 $self->sector(track => $track, sector => $sector);
182             } (0 .. $num_sectors - 1)
183             ];
184             } zip6 @sectors_per_track, @track_numbers;
185 6         18825 return @track_layouts;
186             }
187              
188             =head2 data
189              
190             Fetch disk layout data as a scalar of 683 * 256 bytes:
191              
192             my $data = $d64DiskLayoutObj->data();
193              
194             Update disk layout providing 683 * 256 bytes of scalar data:
195              
196             $d64DiskLayoutObj->data(data => $data);
197              
198             =cut
199              
200             sub data {
201 3     3 1 47 my ($self, %args) = @_;
202 3         28 my $data = $args{data};
203 3         18 my @track_numbers = (1 .. @sectors_per_track);
204             my $result = join '', map {
205 3         60 my ($num_sectors, $track) = @{$_};
  105         64217  
  105         315  
206             join '', map {
207 105         327 my $sector = $_;
  2049         486306  
208 2049 100       4698 if (defined $data) {
209 683         18482 $self->sector_data($track, $sector, split //, substr $data, 0x00, $bytes_per_sector, '');
210             }
211 2049         286540 $self->sector_data($track, $sector);
212             } (0 .. $num_sectors - 1);
213             } zip6 @sectors_per_track, @track_numbers;
214 3         2499 return $result;
215             }
216              
217             =head2 print
218              
219             Print out nicely formatted human-readable form of a track/sector data into any given IO::Handle:
220              
221             $d64DiskLayoutObj->print(fh => $fh, track => $track, sector => $sector);
222              
223             =cut
224              
225             sub print {
226 2     2 1 23 my ($self, %params) = @_;
227 2         24 my $track = $params{track};
228 2         4 my $sector = $params{sector};
229              
230 2   33     12 my $fh = $params{fh} || *STDOUT;
231 2         23 my $stdout = select $fh;
232              
233 2         8 my @data = $self->sector_data($track, $sector);
234              
235             # Fail on invalid track/sector value combinations:
236 2 50       590 carp 'Failed to print track/sector=' . $track . '/' . $sector . ' data' unless @data;
237              
238 2         10 for (my $i = 0x00; $i < 0x0100; $i += 0x10) {
239 32         347 printf q{%02X:}, $i;
240 32         401 for (my $j = 0x00; $j < 0x10; $j++) {
241 512         5836 printf q{ %02x}, ord $data[$i + $j];
242             }
243 32         405 print qq{\n};
244             }
245              
246 2         24 select $stdout;
247 2         29 return;
248             }
249              
250             =head1 BUGS
251              
252             There are no known bugs at the moment. Please report any bugs or feature requests.
253              
254             =head1 EXPORT
255              
256             None. No method is exported into the caller's namespace either by default or explicitly.
257              
258             =head1 SEE ALSO
259              
260             L, L, L.
261              
262             =head1 AUTHOR
263              
264             Pawel Krol, Epawelkrol@cpan.orgE.
265              
266             =head1 VERSION
267              
268             Version 0.02 (2021-01-13)
269              
270             =head1 COPYRIGHT AND LICENSE
271              
272             Copyright 2021 by Pawel Krol .
273              
274             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
275              
276             =cut
277              
278             1;
279              
280             __END__