File Coverage

blib/lib/IO/DiskImage/Floppy.pm
Criterion Covered Total %
statement 9 300 3.0
branch 0 186 0.0
condition 0 21 0.0
subroutine 3 16 18.7
pod 8 8 100.0
total 20 531 3.7


line stmt bran cond sub pod time code
1             #! /usr/bin/perl -w
2             ## ----------------------------------------------------------------------------
3             # IO::DiskImage::Floppy
4             # -----------------------------------------------------------------------------
5             # Mastering programmed by YAMASHINA Hio
6             #
7             # Copyright 2007 YAMASHINA Hio
8             # -----------------------------------------------------------------------------
9             # $Id$
10             # -----------------------------------------------------------------------------
11             package IO::DiskImage::Floppy;
12 1     1   29339 use strict;
  1         3  
  1         114  
13 1     1   8 use warnings;
  1         1  
  1         35  
14 1     1   4 use base qw(Exporter);
  1         7  
  1         5288  
15             our @EXPORT_OK = qw();
16             our %EXPORT_TAGS = ( all => \@EXPORT_OK );
17              
18             our $VERSION = '0.01';
19             our $DEBUG = 0;
20              
21             our $SEEK_SET = 0;
22             our $SEEK_CUR = 1;
23              
24             our @SYSTEM_FORMAT = (
25             # 0x00
26             jmpcode => 'a3',
27             oemlabel => 'A8',
28             bytes_per_sector => 'v',
29             sectors_per_cluster => 'C',
30             reserved_sectors => 'v',
31             # 0x10
32             nr_fat_tables => 'C',
33             max_root_entries => 'v',
34             nr_sectors => 'v',
35             media_descriptor => 'C',
36             fat_size => 'v',
37             sectors_per_track => 'v',
38             nr_heads => 'v',
39             hidden_sectors => 'V',
40             # 0x20
41             large_sectors => 'V',
42             drive_number => 'C',
43             reserved1 => 'a1',
44             boot_signature => 'C',
45             volumeid => 'a4',
46             label => 'A11',
47             fslabel => 'A8',
48             # 0x3e(62)
49             );
50             our @SYSTEM_KEYS = do{ my$i=0; grep{ $i^=1 } @SYSTEM_FORMAT };
51             our $SYSTEM_PACK = join(' ', @{{@SYSTEM_FORMAT}}{@SYSTEM_KEYS});
52              
53             our @DIRENTRY_FORMAT = (
54             basename => 'A8',
55             extension => 'A3',
56             attribute => 'C',
57             reserved1 => 'a1',
58             ctime_10ms => 'C',
59             ctime_time => 'v',
60             ctime_date => 'v',
61             atime_date => 'v',
62             position_high => 'v',
63             mtime_time => 'v',
64             mtime_date => 'v',
65             position => 'v', # cluster start position.
66             size => 'V',
67             );
68             our @DIRENTRY_KEYS = @DIRENTRY_FORMAT[map{$_*2}0..$#DIRENTRY_FORMAT/2];
69             our $DIRENTRY_PACK = join(' ', @{{@DIRENTRY_FORMAT}}{@DIRENTRY_KEYS});
70              
71             our $ATTR_READONLY = 0x01;
72             our $ATTR_HIDDEN = 0x02;
73             our $ATTR_SYSTEM = 0x04;
74             our $ATTR_VOLUMELABEL = 0x08;
75             our $ATTR_DIRECTORY = 0x10;
76             our $ATTR_FILE = 0x20;
77              
78             caller or __PACKAGE__->run(@ARGV);
79              
80             # -----------------------------------------------------------------------------
81             # $pkg->run.
82             #
83             sub run
84             {
85 0     0 1   my $pkg = shift;
86 0           local($|) = 1;
87            
88 0           my $create;
89             my $file;
90 0           my @cmd;
91 0           foreach (@_)
92             {
93 0 0         /^(--create)$/ and $create = $1, next;
94 0 0         /^(-a|--append)$/ and push(@cmd, ['append']), next;
95 0 0         /^(-l|--list)$/ and push(@cmd, ['list']), next;
96 0 0         /^(-x|--extract)$/ and push(@cmd, ['extract']), next;
97 0 0         /^(--ipl)$/ and push(@cmd, ['ipl']), next;
98 0 0         /^(--ipl-address)$/ and push(@cmd, ['ipl_address']), next;
99 0 0         /^-/ and $pkg->usage("unknown option: $_");
100 0 0         defined($file) or $file=$_, next;
101 0 0         @cmd or $pkg->usage("no operation specified for file $_");
102 0           push(@{$cmd[-1]}, $_);
  0            
103             }
104            
105 0 0         defined($file) or die "no file specified";
106 0           my $image = $pkg->new(file=>$file, create=>$create);
107            
108 0           foreach my $cmd (@cmd)
109             {
110 0           my $op = shift @$cmd;
111 0           $image->$op(@$cmd);
112             }
113             }
114              
115             # -----------------------------------------------------------------------------
116             # $pkg->usage(msg).
117             #
118             sub usage
119             {
120 0     0 1   my $pkg = shift;
121 0           my $msg = shift;
122 0           print "$msg\n";
123 0           print "usage:\n";
124 0           print " fdimage [options] image-file [files..]\n";
125 0           print "options:\n";
126 0           print " --create create new image\n";
127 0           print " -a|--append file append file\n";
128 0           print " -l|--list list files contained in image\n";
129 0           print " -x|--extract extract file from image\n";
130 0           print " --ipl ipl.img set ipl image\n";
131 0 0         exit $msg ? 1 : 0;
132             }
133              
134             # -----------------------------------------------------------------------------
135             # $pkg->new();
136             #
137             sub new
138             {
139 0     0 1   my $pkg = shift;
140 0           my $opts = {@_};
141 0 0         my $mode = $opts->{create} ? '+>' : '+<';
142 0           my $file = $opts->{file};
143 0 0         $file or die "no file specified";
144            
145 0 0         open(my $fh, $mode, $file) or die "open failed [$file] : $!";
146 0           binmode($fh);
147            
148 0 0         if( $opts->{create} )
149             {
150 0           $pkg->_format($fh);
151             }
152            
153 0           my $this = bless {}, $pkg;
154 0           $this->{file} = $file;
155 0           $this->{handle} = $fh;
156 0           $this->{system} = undef;
157 0           $this->{ipl_address} = 0x3e;
158 0           $this->_load_system();
159 0           $this;
160             }
161              
162             # -----------------------------------------------------------------------------
163             # $pkg->_format($fh);
164             # format image.
165             #
166             sub _format
167             {
168 0     0     my $pkg = shift;
169 0           my $fh = shift;
170 0           truncate($fh, 2880*512);
171            
172 0           my $system = {
173             # 0x00
174             jmpcode => "\xeb\x3c\x90", # jmp +0x3c; noop;
175             oemlabel => "FDIMG.PL",
176             bytes_per_sector => 512,
177             sectors_per_cluster => 1,
178             reserved_sectors => 1,
179             # 0x10
180             nr_fat_tables => 2,
181             max_root_entries => 224, # 14 sectors.
182             nr_sectors => 2880,
183             media_descriptor => 0xf0, # f8:harddisk.
184             fat_size => 9,
185             sectors_per_track => 19,
186             nr_heads => 2,
187             hidden_sectors => 0,
188             # 0x20
189             large_sectors => 0,
190             drive_number => 0,
191             reserved1 => 0,
192             boot_signature => 0x29, # (?)
193 0           volumeid => pack("C4",map{rand(256)}1..4),
194             label => 'NO NAME ',
195             fslabel => 'FAT12 ',
196             # 0x3e(62)
197             };
198 0           my $data = pack($SYSTEM_PACK, @$system{@SYSTEM_KEYS});
199 0           $data .= "\0"x(512-2-length($data));
200 0           $data .= "\x55\xaa";
201 0 0         seek($fh, 0, $SEEK_SET) or die "seek failed: $!";
202 0           print $fh $data;
203            
204             # FAT reserved cluster.
205 0 0         seek($fh, 512, $SEEK_SET) or die "seek failed: $!";
206 0           print $fh "\xf0\xff\xff";
207             # FAT reserved cluster(spare).
208 0 0         seek($fh, 10*512, $SEEK_SET) or die "seek failed: $!";
209 0           print $fh "\xf0\xff\xff";
210            
211 0           $fh;
212             }
213              
214             # -----------------------------------------------------------------------------
215             # $obj->_load_system()
216             #
217             sub _load_system()
218             {
219 0     0     my $this = shift;
220 0 0         seek($this->{handle}, 0, $SEEK_SET) or die "seek failed: $!";
221 0           my $read_len = read($this->{handle}, my $data, 62);
222 0 0         defined($read_len) or die "read failed: $!";
223 0 0         $read_len!=62 and die "read few data ($read_len/62)";
224            
225 0           my $system = {};
226 0           $system->{header} = $data;
227 0           @$system{@SYSTEM_KEYS} = unpack($SYSTEM_PACK, $data);
228 0           $this->{system} = $system;
229             #print Dumper($this->{system});use Data::Dumper;
230 0           $this;
231             }
232              
233             # -----------------------------------------------------------------------------
234             # $obj->list()
235             #
236             sub list()
237             {
238 0     0 1   my $this = shift;
239             $this->_list(sub{
240 0     0     my $dirent = shift;
241             # drwxr-xr-x root/root 0 2006-11-07 18:28:52 l_cc_c_9.1.045/
242 0           my $is_dir = $dirent->{attribute} & $ATTR_DIRECTORY;
243 0           my $is_readonly = $dirent->{attribute} & $ATTR_READONLY;
244 0           my $attr = '';
245 0 0         $attr .= $is_dir ? 'd' : '-';
246 0           $attr .= 'r';
247 0 0         $attr .= !$is_readonly ? 'w' : '-';
248 0           $attr .= 'x';
249 0           my $mtime = sprintf('%04d-%02d-%02d %02d:%02d:%02d',
250             ($dirent->{mtime_date}>>9)+1980,
251             ($dirent->{mtime_date}>>5)&15,
252             ($dirent->{mtime_date}>>0)&31,
253             ($dirent->{mtime_time}>>11)&31,
254             ($dirent->{mtime_time}>>5)&63,
255             ($dirent->{mtime_time}&31)<<1);
256 0 0         my $dot = $dirent->{extension} ne '' ? '.' : '';
257 0           print sprintf("%s %7d %s %-8s%s%-3s\n", $attr, $dirent->{size}, $mtime, $dirent->{basename}, $dot, $dirent->{extension});
258 0           });
259             }
260              
261             # -----------------------------------------------------------------------------
262             # $obj->_list(\&callback)
263             #
264             sub _list
265             {
266 0     0     my $this = shift;
267 0 0         my $cb = shift or die "no callback for _list";
268            
269 0           my $system = $this->{system};
270 0           my $pos = $system->{reserved_sectors} + $system->{fat_size} * $system->{nr_fat_tables};
271 0 0         seek($this->{handle}, $pos*512, $SEEK_SET) or die "seek failed: $!";
272 0           for( my $i=0; $i<$system->{max_root_entries}-1; ++$i )
273             {
274 0           my $read_len = read($this->{handle}, my $data, 32);
275 0 0         defined($read_len) or die "read failed: $!";
276 0 0         $read_len!=32 and die "read few data ($read_len/32)";
277 0           my %dirent;
278 0           @dirent{@DIRENTRY_KEYS} = unpack($DIRENTRY_PACK, $data);
279 0 0         $dirent{attribute}==0x0f and next; # Long File Name.
280 0 0         $dirent{basename} =~ /^\0/ and last;
281 0 0         $dirent{basename} =~ /[^ ]/ or next;
282 0           $cb->(\%dirent, @_);
283             }
284             }
285              
286             # -----------------------------------------------------------------------------
287             # $obj->extract($file)
288             #
289             sub extract
290             {
291 0     0 1   my $this = shift;
292 0           my $file = shift;
293 0 0         defined($file) or die "no file specified for extract";
294 0           my $file_uc = $file; $file_uc =~ tr/a-z/A-Z/;
  0            
295 0           my $found;
296             $this->_list(sub{
297 0     0     my $dirent = shift;
298 0           my $got = $dirent->{basename};
299 0 0         $dirent->{extension} ne '' and $got .= ".$dirent->{extension}";
300 0           $got =~ tr/a-z/A-Z/;
301 0 0         $got eq $file_uc and $found = $dirent;
302 0           });
303            
304 0 0         if( !$found )
305             {
306 0           print "no such file in image [$file]\n";
307 0           return;
308             }
309            
310             # found.
311 0           my $system = $this->{system};
312 0           my $fatsect = $system->{reserved_sectors};
313 0           my $basesect = $system->{reserved_sectors}
314             + $system->{fat_size} * $system->{nr_fat_tables}
315             + $system->{max_root_entries} * 32 / 512;
316 0           my $data = '';
317 0           my $cluster = $found->{position};
318 0           my $ENDMARK = 0x0FF0;
319             do
320 0           {
321             #print sprintf("cluster %d (0x%x)\n", $cluster, $cluster);
322 0 0 0       if( $cluster<=0x0001 || $cluster>=$ENDMARK )
323             {
324 0           die "invalid cluster index found: $cluster";
325             }
326             # read cluster data.
327 0 0         seek($this->{handle}, ($basesect+$cluster-2)*512, $SEEK_SET) or die "seek failed: $!";
328 0           my $read_len = read($this->{handle}, $data, 512, length($data));
329 0 0         defined($read_len) or die "read failed: $!";
330 0 0         $read_len!=512 and die "read few data ($read_len/512)";
331             # find next cluster.
332 0           my $offset = int($cluster*3/2);
333             #print sprintf("x1.5 %d (0x%x)\n", $offset, $offset);
334 0           my $r = $fatsect+int($offset/512);
335             #print sprintf("sect %d (0x%x)\n", $r, $r);
336 0 0         seek($this->{handle}, ($fatsect+int($offset/512))*512, $SEEK_SET) or die "seek failed: $!";
337 0           $read_len = read($this->{handle}, my $fat, 512);
338 0 0         defined($read_len) or die "read failed: $!";
339 0 0         $read_len!=512 and die "read few data ($read_len/512)";
340 0           my $odd = $cluster & 1;
341 0           $cluster = unpack("v", substr($fat, $offset&511, 2));
342             #print sprintf("next %d (0x%x)\n", $cluster, $cluster);
343 0 0         $odd and $cluster >>= 4;
344             #print sprintf("next %d (0x%x)\n", $cluster, $cluster);
345 0           $cluster &= 0x0FFF;
346             #print sprintf("next %d (0x%x)\n", $cluster, $cluster);
347             } while( $cluster<0x0FF0 );
348 0           $data = substr($data, 0, $found->{size});
349            
350 0 0         open(my $fh, '>', $file) or die "could not open file for output [$file] : $!";
351 0           print $fh $data;
352 0           close $fh;
353 0           return $this;
354             }
355              
356             # -----------------------------------------------------------------------------
357             # $obj->append($file)
358             #
359             sub append
360             {
361 0     0 1   my $this = shift;
362 0           my $file = shift;
363 0 0         defined($file) or die "no file specified for extract";
364 0 0         $file =~ /^(\w{1,8})(?:\.\w{1,3})$/ or die "not 8.3 file name [$file]";
365 0           my $file_uc = $file; $file_uc =~ tr/a-z/A-Z/;
  0            
366            
367 0           my @st;
368 0           my $data = do {
369 0 0         open(my $fh, '<', $file) or die "could not open file [$file]: $!";
370 0           @st = stat($fh);
371 0           local($/) = undef;
372 0           my $tmp = <$fh>;
373 0           close $fh;
374 0           $tmp;
375             };
376            
377 0           my $system = $this->{system};
378            
379             # find data spaces.
380 0           my @spaces;
381             {
382 0           my $table = '';
  0            
383 0           my $available_sectors = $system->{nr_sectors}
384             - $system->{reserved_sectors}
385             - ($system->{fat_size} * $system->{nr_fat_tables})
386             - ($system->{max_root_entries} * 32 / 512);
387 0 0         $DEBUG and print STDERR "find sectors, fat at $system->{reserved_sectors}, $available_sectors clusters\n";
388 0 0         seek($this->{handle}, $system->{reserved_sectors}*512, $SEEK_SET) or die "seek failed: $!";
389 0           for( my $i=0; $i<$available_sectors; ++$i )
390             {
391 0 0         if( length($table)<2 )
392             {
393 0           my $read_len = read($this->{handle}, $table, 512, length $table);
394 0 0         defined($read_len) or die "read failed: $!";
395 0 0         $read_len!=512 and die "read few data ($read_len/512)";
396 0 0         $i==2 and $table =~ s/^...//s;
397             #print unpack("H*", $table)."\n";
398             }
399 0           my $cluster = unpack("v", $table);
400             #print sprintf("%d %04x\n", $i, $cluster);
401 0 0         if( $i&1 )
402             {
403 0           $table =~ s/^..//s;
404 0           $cluster >>= 4;
405             }else
406             {
407 0           $table =~ s/^.//s;
408             }
409 0           $cluster &= 0x0FFF;
410 0 0         $DEBUG and print sprintf("$i: %03x \n", $cluster);
411 0 0         $cluster==0 or next;
412 0           push(@spaces, $i);
413 0 0         @spaces*512>= length($data) and last;
414             }
415 0 0         if( @spaces*512
416             {
417 0           die "no space left";
418             }
419             }
420            
421             # find directory entry.
422 0           my $newentry;
423             my $space;
424 0           my $sect = $system->{reserved_sectors} + $system->{fat_size} * $system->{nr_fat_tables};
425 0 0         seek($this->{handle}, $sect*512, $SEEK_SET) or die "seek failed: $!";
426 0           my $index = 0;
427 0           for( $index=0; $index<$system->{max_root_entries}; ++$index )
428             {
429 0           my $read_len = read($this->{handle}, my $data, 32);
430 0 0         defined($read_len) or die "read failed: $!";
431 0 0         $read_len!=32 and die "read few data ($read_len/32)";
432 0           my %dirent;
433 0           @dirent{@DIRENTRY_KEYS} = unpack($DIRENTRY_PACK, $data);
434 0 0         $dirent{attribute}==0x0f and next; # Long File Name.
435 0 0 0       $dirent{basename} eq '' and $space||=$index,last;
436 0 0 0       $dirent{basename} =~ /^\0/ and $space||=$index,last;
437 0 0 0       $dirent{basename} =~ /^\xe5/ and $space||=$index,next;
438 0 0 0       $dirent{basename} =~ /^\x05/ and $space||=$index,next;
439 0 0 0       $dirent{basename} =~ /[^ ]/ or $space||=$index,next;
440             #
441 0           my $got = $dirent{basename};
442 0 0         $dirent{extension} ne '' and $got .= ".$dirent{extension}";
443 0           $got =~ tr/a-z/A-Z/;
444 0 0         if( $got eq $file_uc )
445             {
446 0           $newentry = \%dirent;
447 0           last;
448             }
449             }
450 0 0 0       if( !defined($space) && !$newentry )
451             {
452 0           die "no space on root entry";
453             }
454            
455             # update data space.
456             {
457 0           my $basesect = $system->{reserved_sectors}
  0            
458             + $system->{fat_size} * $system->{nr_fat_tables}
459             + $system->{max_root_entries} * 32 / 512;
460 0           foreach my $i (0..$#spaces)
461             {
462 0 0         seek($this->{handle}, ($basesect+$spaces[$i]-2)*512, $SEEK_SET) or die "seek failed: $!";
463 0           print {$this->{handle}} substr($data, $i*512, 512);
  0            
464             }
465             }
466             # update fat entry.
467             {
468 0           my $fatsect = $system->{reserved_sectors};
  0            
469 0 0         seek($this->{handle}, $fatsect*512, $SEEK_SET) or die "seek failed: $!";
470 0           my $read_len = read($this->{handle}, my $fat_table, 512*$system->{fat_size});
471 0 0         defined($read_len) or die "read failed: $!";
472 0 0         $read_len!=512*$system->{fat_size} and die "read few data ($read_len/512*$system->{fat_size})";
473            
474 0           foreach my $i (0..$#spaces)
475             {
476 0           my $odd = $spaces[$i] & 1;
477 0           my $offset = int($spaces[$i]*3/2);
478 0           my $cluster = unpack("v", substr($fat_table, $offset, 2));
479 0 0         my $next = $i==$#spaces ? 0x0FFF : $spaces[$i+1];
480 0 0         if( !$odd )
481             {
482 0           $cluster &= 0xF000;
483 0           $cluster |= $next;
484             }else
485             {
486 0           $cluster &= 0x000F;
487 0           $cluster |= $next<<4;
488             }
489 0           substr($fat_table, $offset, 2, pack('v', $cluster));
490             }
491 0 0         seek($this->{handle}, $fatsect*512, $SEEK_SET) or die "seek failed: $!";
492 0           print {$this->{handle}} $fat_table;
  0            
493             }
494            
495             # update directory entry.
496 0 0         if( $newentry )
497             {
498             # update.
499 0 0         seek($this->{handle}, -32, $SEEK_CUR) or die "seek failed: $!";
500             }else
501             {
502             # create.
503 0           my $pos = $sect*512 + $space*32;
504             #print sprintf("create: sect = %d (0x%x)\n", $sect, $sect);
505             #print sprintf("create: space = %d (0x%x)\n", $space, $space);
506             #print sprintf("create: pos = %d (0x%x)\n", $pos, $pos);
507 0 0         seek($this->{handle}, $pos, $SEEK_SET) or die "seek failed: $!";
508 0           my ($base,$ext)= split(/\./, $file);
509 0           $newentry->{basename} = uc($base);
510 0 0         $newentry->{extension} = defined($ext) ? uc($ext) : '';
511 0           $newentry->{attribute} = $ATTR_FILE;
512 0           $newentry->{reserved1} = "\0";
513 0           $newentry->{position_high} = 0;
514             }
515 0           my ($ST_ATIME, $ST_MTIME, $ST_CTIME) = (8, 9, 10);
516 0           my @ctime = gmtime($st[$ST_CTIME]);
517 0           my @mtime = gmtime($st[$ST_MTIME]);
518 0           my @atime = gmtime($st[$ST_ATIME]);
519 0           $newentry->{ctime_10ms} = ($ctime[0]%2)*100;
520 0           $newentry->{ctime_time} = ($ctime[2]<<11) + ($ctime[1]<<5) + ($ctime[0]>>1);
521 0           $newentry->{ctime_date} = (($ctime[5]-1980)<<9) + (($ctime[4]+1)<<5) + $ctime[3];
522 0           $newentry->{atime_date} = (($atime[5]-1980)<<9) + (($atime[4]+1)<<5) + $atime[3];
523 0           $newentry->{mtime_time} = ($mtime[2]<<11) + ($mtime[1]<<5) + ($mtime[0]>>1);
524 0           $newentry->{mtime_date} = (($mtime[5]-1980)<<9) + (($mtime[4]+1)<<5) + $mtime[3];
525 0           $newentry->{position} = $spaces[0];
526 0           $newentry->{size} = length $data;
527            
528 0           my $de = pack($DIRENTRY_PACK, @$newentry{@DIRENTRY_KEYS});
529 0 0         length($de)==32 or die "direntry size not 32";
530 0           print {$this->{handle}} $de;
  0            
531            
532             #print "updated.\n";
533 0           $this;
534             }
535              
536             # -----------------------------------------------------------------------------
537             # $obj->ipl_address()
538             # $obj->ipl_address($addr)
539             #
540             sub ipl_address
541             {
542 0     0 1   my $this = shift;
543 0           my $addr = shift;
544 0 0         if( defined($addr) )
545             {
546 0 0         $addr =~ /^0x([0-9a-fA-F]+)$/ and $addr = hex($1);
547 0 0         $addr =~ /^(\d+$)/ or die "ipl_address is not numeric: $addr";
548 0 0         $addr>=0xfe and die "ipl_address too large: $addr";
549 0           $this->{ipl_address} = $addr;
550             }else
551             {
552 0           my $hex = sprintf('0x%x', $this->{ipl_address});
553 0           print "ipl-address: $hex ($this->{ipl_address})\n";
554             }
555 0           $this;
556             }
557              
558             # -----------------------------------------------------------------------------
559             # $obj->ipl($file)
560             #
561             sub ipl
562             {
563 0     0 1   my $this = shift;
564 0           my $file = shift;
565 0 0         if( defined($file) )
566             {
567 0 0         open(my $fh, '<', $file) or die "could not open file [$file]: $!";
568 0           local($/);
569 0           my $data = <$fh>;
570 0           close $fh;
571 0           my $size = length($data);
572 0 0         $this->{ipl_address}+$size >= 0xfe and die "ipl image too large: $size";
573 0 0         seek($this->{handle}, $this->{ipl_address}, $SEEK_SET) or die "seek failed: $!";
574 0           print {$this->{handle}} $data;
  0            
575 0           $this;
576             }else
577             {
578 0           my $size = 0xfe - $this->{ipl_address};
579 0 0         seek($this->{handle}, $this->{ipl_address}, $SEEK_SET) or die "seek failed: $!";
580 0           my $read_len = read($this->{handle}, my $data, $size);
581 0 0         defined($read_len) or die "read failed: $!";
582 0 0         $read_len!=$size and die "read few data ($read_len/$size)";
583 0           binmode(STDOUT);
584 0           print $data;
585             }
586             }
587              
588             # -----------------------------------------------------------------------------
589             # End of Module.
590             # -----------------------------------------------------------------------------
591             # -----------------------------------------------------------------------------
592             # End of File.
593             # -----------------------------------------------------------------------------
594             __END__