Subject: | Win32: error reading and writing image files (binmode) |
In Win32, binmode() is needed to read and write binary files.
The ROM image files read by this module are not correctly represented in
the object if they contain LF bytes, because of the Win32 translation of
end-of-line characters.
The same happens to the RAM image files stored on disk.
The attached patch and test file solve this issue:
* add binmode() after each open() call
* add either 'binmode $fh' or '$fh->binmode' in _readROM() to cope with
either filehandles or IO::Handle objects
* the test file read_write_binary.pl implements binary read and write
functions to be used in the tests; maybe File::Slurp should be used
instead
* 02-file.t is modified to exercise the bug in Win32, by pokeing a CR
and a LF bytes
* several test are added to 03-banked-memory-ROM.t to read a problem ROM
file (with CR and LF bytes) by file name, by file handle and by
IO::File; the dependency to IO::File was also added to Makefile.PL
I hope this patch is usefull and can get into the next version of the
module.
Thank you.
Paulo Custodio
Subject: | read_write_binary.pl |
#!perl
use strict;
use warnings;
use Test::More;
sub read_binary {
my($file) = @_;
local $/ = undef;
open(my $fh, $file) || die("Couldn't read $file\n");
binmode($fh);
return scalar(<$fh>);
}
sub write_binary {
my($file, $bytes) = @_;
open(my $fh, '>', $file) || die("Couldn't create $file\n");
binmode($fh);
print $fh $bytes;
}
# test these functions
unlink 'bytes.ram';
write_binary('bytes.ram', "\x01\x0D\x0A");
is(-s 'bytes.ram', 3, "binary file has correct size ...");
$_ = read_binary('bytes.ram');
is($_, "\x01\x0D\x0A", "... and correct content");
ok(unlink('bytes.ram'), "bytes.ram deleted");
1;
Subject: | CPU-Emulator-Memory-1.1001-patch02-RT.txt |
diff -cr CPU-Emulator-Memory-1.1001/MANIFEST CPU-Emulator-Memory-1.1001_02/MANIFEST
*** CPU-Emulator-Memory-1.1001/MANIFEST 2008-02-28 22:20:36.000000000 +0000
--- CPU-Emulator-Memory-1.1001_02/MANIFEST 2010-10-22 17:59:35.551993000 +0100
***************
*** 15,18 ****
t/coverage.sh
t/pod-coverage.t
t/pod.t
! META.yml Module meta-data (added by MakeMaker)
--- 15,19 ----
t/coverage.sh
t/pod-coverage.t
t/pod.t
! t/read_write_binary.pl
! META.yml Module meta-data (added by MakeMaker)
diff -cr CPU-Emulator-Memory-1.1001/Makefile.PL CPU-Emulator-Memory-1.1001_02/Makefile.PL
*** CPU-Emulator-Memory-1.1001/Makefile.PL 2008-02-28 22:20:05.000000000 +0000
--- CPU-Emulator-Memory-1.1001_02/Makefile.PL 2010-10-22 18:00:58.419732800 +0100
***************
*** 5,10 ****
--- 5,11 ----
VERSION => 1.1001,
PREREQ_PM => {
'Scalar::Util' => 0,
+ 'IO::File' => 0,
'IO::Scalar' => 0
}
);
diff -cr CPU-Emulator-Memory-1.1001/lib/CPU/Emulator/Memory/Banked.pm CPU-Emulator-Memory-1.1001_02/lib/CPU/Emulator/Memory/Banked.pm
*** CPU-Emulator-Memory-1.1001/lib/CPU/Emulator/Memory/Banked.pm 2008-02-28 22:15:52.000000000 +0000
--- CPU-Emulator-Memory-1.1001_02/lib/CPU/Emulator/Memory/Banked.pm 2010-10-22 18:03:27.158240100 +0100
***************
*** 251,256 ****
--- 251,262 ----
if(reftype($file) eq 'GLOB') {
local $/ = undef;
+ if (eval {$file->can('binmode')}) {
+ $file->binmode; # IO::HANDLE
+ }
+ else {
+ binmode $file; # file handle
+ }
my $contents = <$file>;
die("data in filehandle is wrong size (got ".length($contents).", expected $size)\n") unless(length($contents) == $size);
return $contents;
diff -cr CPU-Emulator-Memory-1.1001/lib/CPU/Emulator/Memory.pm CPU-Emulator-Memory-1.1001_02/lib/CPU/Emulator/Memory.pm
*** CPU-Emulator-Memory-1.1001/lib/CPU/Emulator/Memory.pm 2008-02-28 19:41:29.000000000 +0000
--- CPU-Emulator-Memory-1.1001_02/lib/CPU/Emulator/Memory.pm 2010-10-22 18:04:12.964860100 +0100
***************
*** 172,177 ****
--- 172,178 ----
my($self, $file, $size) = @_;
local $/ = undef;
open(my $fh, $file) || die("Couldn't read $file\n");
+ binmode($fh);
my $contents = <$fh>;
die("$file is wrong size\n") unless(length($contents) == $size);
close($fh);
***************
*** 192,197 ****
--- 193,199 ----
sub _writeRAM {
my($self, $file, $contents) = @_;
open(my $fh, '>', $file) || die("Can't write $file\n");
+ binmode($fh);
print $fh $contents || die("Can't write $file\n");
close($fh);
}
diff -cr CPU-Emulator-Memory-1.1001/t/02-file.t CPU-Emulator-Memory-1.1001_02/t/02-file.t
*** CPU-Emulator-Memory-1.1001/t/02-file.t 2008-02-14 13:29:03.000000000 +0000
--- CPU-Emulator-Memory-1.1001_02/t/02-file.t 2010-10-22 16:32:10.243978500 +0100
***************
*** 1,25 ****
use strict;
$^W = 1;
! use Test::More tests => 5;
use CPU::Emulator::Memory;
unlink 'newfile.ram';
my $memory = CPU::Emulator::Memory->new(file => 'newfile.ram');
# NB using {0xHEXSTUFF} in regexes doesn't work.
# and the repeated {30000}...{30000} is cos there's a 2^15 - 2 limit
! $/ = undef;
! open(my $fh, 'newfile.ram') || die("Couldn't open newfile.ram\n");
! ok(<$fh> =~ /^\000{30000}\000{30000}\000{5536}$/, "New file created as all zeroes");
! close($fh);
ok($memory->peek(0) == 0, "Peek confirms a zero");
! ok($memory->poke(0, 1) && $memory->peek(0) == 1, "Poke works ...");
! open($fh, 'newfile.ram');
! ok(<$fh> =~ /^\001\000{30000}\000{30000}\000{5535}$/, "... and is reflected in the file");
! close($fh);
undef $memory;
--- 1,28 ----
use strict;
$^W = 1;
! use Test::More tests => 11;
use CPU::Emulator::Memory;
+ require 't/read_write_binary.pl';
+
unlink 'newfile.ram';
my $memory = CPU::Emulator::Memory->new(file => 'newfile.ram');
# NB using {0xHEXSTUFF} in regexes doesn't work.
# and the repeated {30000}...{30000} is cos there's a 2^15 - 2 limit
! $_ = read_binary('newfile.ram');
! ok(/^\000{30000}\000{30000}\000{5536}$/, "New file created as all zeroes");
!
ok($memory->peek(0) == 0, "Peek confirms a zero");
! ok($memory->poke(0, 1) && $memory->peek(0) == 1, "Poke works ...");
! ok($memory->poke(1, 13) && $memory->peek(1) == 13, "Poke works ...");
! ok($memory->poke(2, 10) && $memory->peek(2) == 10, "Poke works ...");
! is(-s 'newfile.ram', 0x10000, "file is correct size");
! $_ = read_binary('newfile.ram');
! ok(/^\001\015\012\000{30000}\000{30000}\000{5533}$/s, "... and is reflected in the file");
undef $memory;
diff -cr CPU-Emulator-Memory-1.1001/t/03-banked-memory-ROM.t CPU-Emulator-Memory-1.1001_02/t/03-banked-memory-ROM.t
*** CPU-Emulator-Memory-1.1001/t/03-banked-memory-ROM.t 2008-02-28 19:41:29.000000000 +0000
--- CPU-Emulator-Memory-1.1001_02/t/03-banked-memory-ROM.t 2010-10-22 17:21:28.852201300 +0100
***************
*** 1,12 ****
use strict;
$^W = 1;
! use Test::More tests => 19;
!
! undef $/;
use CPU::Emulator::Memory::Banked;
use IO::Scalar;
unlink 'ramfile.ram', 'romfile.rom';
my $memory = CPU::Emulator::Memory::Banked->new(file => 'ramfile.ram');
--- 1,13 ----
use strict;
$^W = 1;
! use Test::More tests => 33;
use CPU::Emulator::Memory::Banked;
use IO::Scalar;
+ use IO::File;
+
+ require 't/read_write_binary.pl';
unlink 'ramfile.ram', 'romfile.rom';
my $memory = CPU::Emulator::Memory::Banked->new(file => 'ramfile.ram');
***************
*** 14,22 ****
# NB using {0xHEXSTUFF} in regexes doesn't work.
# and the repeated {30000}...{30000} is cos there's a 2^15 - 2 limit
! open(my $fh, '>', 'romfile.rom') || die("Can't create ROM file for testing\n");
! print $fh 'This is a ROM';
! close($fh);
eval { $memory->bank(
address => 0,
--- 15,21 ----
# NB using {0xHEXSTUFF} in regexes doesn't work.
# and the repeated {30000}...{30000} is cos there's a 2^15 - 2 limit
! write_binary('romfile.rom', 'This is a ROM');
eval { $memory->bank(
address => 0,
***************
*** 54,64 ****
ok($memory->peek(1) == 1, "With writethrough, RAM gets updated");
ok($memory->peek(2) == 1, "poke8 worked too");
! open($fh, 'romfile.rom') || die("Can't read romfile.rom\n");
! ok(<$fh> eq 'This is a ROM', "ROM files don't get altered");
! close($fh);
! open($fh, 'ramfile.ram') || die("Can't read ramfile.ram\n");
! ok(<$fh> =~ /^\000\001{2}\000{30000}\000{30000}\000{5532}\001$/, "With writethrough, RAM file gets updated correctly");
$memory->bank(
address => 0,
--- 53,63 ----
ok($memory->peek(1) == 1, "With writethrough, RAM gets updated");
ok($memory->peek(2) == 1, "poke8 worked too");
! $_ = read_binary('romfile.rom');
! ok($_ eq 'This is a ROM', "ROM files don't get altered");
!
! $_ = read_binary('ramfile.ram');
! ok(/^\000\001{2}\000{30000}\000{30000}\000{5532}\001$/, "With writethrough, RAM file gets updated correctly");
$memory->bank(
address => 0,
***************
*** 104,108 ****
--- 103,148 ----
);
ok($memory->peek(0) == ord('A'), "ROM 'file' can also be a filehandle");
+ # test banked ROM with chr(13) and chr(10) inside
+ # - needs binmode to read/write correctly in Win32
+ write_binary('romfile.rom', "\x01\x0D\x0A");
+ is(-s 'romfile.rom', 3, "binmode was used, size is OK");
+ $memory->bank(
+ address => 0,
+ size => 3,
+ type => 'ROM',
+ file => 'romfile.rom'
+ );
+ is($memory->peek(0), 1, "peek 1");
+ is($memory->peek(1), 13, "peek 13");
+ is($memory->peek(2), 10, "peek 10");
+
+ # test with filehandle
+ open(my $fh, 'romfile.rom') || die("Couldn't read romfile.rom\n");
+ $memory->bank(
+ address => 0,
+ size => 3,
+ type => 'ROM',
+ file => $fh
+ );
+ is($memory->peek(0), 1, "peek 1");
+ is($memory->peek(1), 13, "peek 13");
+ is($memory->peek(2), 10, "peek 10");
+ close $fh;
+
+ # test with IO::File
+ $memory->bank(
+ address => 0,
+ size => 3,
+ type => 'ROM',
+ file => IO::File->new('romfile.rom', 'r')
+ );
+ is($memory->peek(0), 1, "peek 1");
+ is($memory->peek(1), 13, "peek 13");
+ is($memory->peek(2), 10, "peek 10");
+
+ undef $memory; # to release IO::File handle
+ ok(unlink('romfile.rom'), "romfile.rom deleted");
+
__DATA__
A
Only in CPU-Emulator-Memory-1.1001_02/t: read_write_binary.pl