File Coverage

blib/lib/Games/NES/ROM.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Games::NES::ROM;
2              
3 2     2   45813 use Moose;
  0            
  0            
4             use Module::Runtime ();
5             use Try::Tiny;
6             use FileHandle;
7              
8             our $VERSION = '0.08';
9              
10             has 'filename' => ( is => 'rw' );
11              
12             has 'filehandle' => ( is => 'rw', isa => 'FileHandle', lazy_build => 1 );
13              
14             has 'id' => ( is => 'ro' );
15              
16             has 'title' => ( is => 'rw' );
17              
18             has 'prg_banks' => ( is => 'rw', isa => 'ArrayRef', default => sub { [] } );
19              
20             has 'chr_banks' => ( is => 'rw', isa => 'ArrayRef', default => sub { [] } );
21              
22             has 'has_sram' => ( is => 'rw', isa => 'Bool', default => 0 );
23              
24             has 'mirroring' => ( is => 'rw', isa => 'Int', default => 0 );
25              
26             has 'mapper' => ( is => 'rw' );
27              
28             sub _build_filehandle {
29             my $self = shift;
30             my $file = $self->filename;
31             my $fh = FileHandle->new( $file, '<' );
32              
33             die "Unable to open ${file}: $!" unless defined $fh;
34              
35             binmode( $fh );
36             return $fh;
37             }
38              
39             __PACKAGE__->meta->make_immutable;
40              
41             sub load {
42             my( $class, $file ) = @_;
43              
44             for( qw( INES UNIF ) ) {
45             my $class = 'Games::NES::ROM::Format::' . $_;
46             Module::Runtime::require_module( $class );
47              
48             my $rom = try {
49             $class->new( filename => $file );
50             };
51              
52             return $rom if $rom;
53             }
54              
55             die "${file} is not an NES rom";
56             }
57              
58             sub prg_count {
59             return scalar @{ shift->prg_banks };
60             }
61              
62             sub chr_count {
63             return scalar @{ shift->chr_banks };
64             }
65              
66             sub sha1 {
67             my $self = shift;
68             require Digest::SHA1;
69             return Digest::SHA1::sha1_hex( @{ $self->prg_banks }, @{ $self->chr_banks } );
70             }
71              
72             sub crc {
73             my $self = shift;
74             require Digest::CRC;
75             return Digest::CRC::crc32_hex( join( '', @{ $self->prg_banks }, @{ $self->chr_banks } ) );
76             }
77              
78             sub sprite {
79             my( $self, $chr, $offset ) = @_;
80              
81             die 'invalid CHR bank' if $chr > $self->chr_count - 1 or $chr < 0;
82             die 'invalid sprite index' if $offset > 512 or $offset < 0;
83            
84             my $bank = $self->chr_banks->[ $chr ];
85             my $start = 16 * $offset;
86             my @channel_a = unpack( 'C*', substr( $bank, $start, 8 ) );
87             my @channel_b = unpack( 'C*', substr( $bank, $start + 8, 8 ) );
88              
89             my $composite = '';
90              
91             for my $i ( 0..7 ) {
92             for my $j ( reverse 0..7 ) {
93             $composite .= pack( 'C', $self->_combine_bits( $channel_a[ $i ], $channel_b[ $i ], $j ) );
94             }
95             }
96            
97             return $composite;
98             }
99              
100             sub _combine_bits {
101             my $self = shift;
102             my( $chan_a, $chan_b, $offset ) = @_;
103              
104             return ( ( $chan_a >> $offset ) & 1 ) | ( ( ( $chan_b >> $offset ) & 1 ) << 1 );
105             }
106              
107             1;
108              
109             __END__
110              
111             =head1 NAME
112              
113             Games::NES::ROM - View information about an NES game from a ROM file
114              
115             =head1 SYNOPSIS
116              
117             use Games::NES::ROM;
118            
119             # Read in the ROM without having to know its format
120             my $rom = Games::NES::ROM->load( 'file.nes' );
121              
122             # Specifically read in an iNES file
123             $rom = Games::NES::ROM::INES->new( filename => 'file.nes' );
124              
125             # Access the details
126             print 'PRG Banks: ', $rom->prg_count, "\n";
127             print 'CHR Banks: ', $rom->prg_count, "\n";
128             # etc...
129            
130             # View the SHA-1 & CRC checksums
131             print 'SHA1: ', $rom->sha1, "\n";
132             print ' CRC: ', $rom->crc, "\n";
133              
134             =head1 DESCRIPTION
135              
136             This module loads the details of an NES rom file. It is primarily meant to be
137             used a base class for more specific file formats. Those formats include:
138              
139             =over 4
140              
141             =item * L<Universal NES Image File (UNIF)|Games::NES::ROM::Format::UNIF>
142              
143             =item * L<iNES|Games::NES::ROM::Format::INES>
144              
145             =back
146              
147             =head1 INSTALLATION
148              
149             perl Makefile.PL
150             make
151             make test
152             make install
153              
154             =head1 METHODS
155              
156             =head2 load( $filename )
157              
158             Attemps to read the ROM structure into memory using all available file
159             formats until success.
160              
161             =head2 prg_count( )
162              
163             Returns the number of PRG banks for this ROM.
164              
165             =head2 chr_count( )
166              
167             Returns the number of CHR banks for this ROM.
168              
169             =head2 sha1( )
170              
171             Returns the SHA-1 checksum for the PRG and CHR data.
172              
173             =head2 crc( )
174              
175             Returns the CRC checksum for the PRG and CHR data.
176              
177             =head2 sprite( $chr_bank, $index )
178              
179             Returns the raw (composite) sprite in the specified
180             CHR bank at the specified array index.
181              
182             =head1 BASE ATTRIBUTES
183              
184             The following base attributes are available for all file formats:
185              
186             =over 4
187              
188             =item * filename - The filename from which data was loaded
189              
190             =item * id - A string found at the beginning of a file to identify the file format
191              
192             =item * title - The game's title, if available
193              
194             =item * prg_banks - An arrayref of PRG bank data
195              
196             =item * chr_banks - An arrayref of CHR bank data
197              
198             =item * has_sram - Boolean value to determine if the ROM is battery backed
199              
200             =item * mapper - A value indicating what memory mapper to use with the ROM
201              
202             =item * mirroring - A value indicating what time of mirroring is used in the ROM
203              
204             =back
205              
206             =head2 NOTES
207              
208             =over 4
209              
210             =item * mapper - iNES uses integer IDs, UNIF uses string IDs
211              
212             =item * mirroring - An integer ID, using the UNIF list as a basis
213              
214             =back
215              
216             Each file format will have an extended set of attributes specific to its data
217             structure. Please consult their documentation for more information.
218              
219             =head1 SEE ALSO
220              
221             =over 4
222              
223             =item * L<Games::NES::ROM::Format::INES>
224              
225             =item * L<Games::NES::ROM::Format::UNIF>
226              
227             =back
228              
229             =head1 AUTHOR
230              
231             Brian Cassidy E<lt>bricas@cpan.orgE<gt>
232              
233             =head1 COPYRIGHT AND LICENSE
234              
235             Copyright 2007-2013 by Brian Cassidy
236              
237             This library is free software; you can redistribute it and/or modify
238             it under the same terms as Perl itself.
239              
240             =cut
241