#!/usr/bin/perl -l use strict; use warnings; package My::GbCart; sub new { my ($class, $cart) = @_; bless $cart => $class; } { my $START = 0x0104; my $END = 0x0133; sub nintendo_graphic { my $self = shift; my @cart = @$self; return join '', @cart[$START .. $END]; } } { my $TITLE_START = 0x0134; my $TITLE_END = 0x0142; sub title { my $self = shift; my @cart = @$self; return join '', map( chr, @cart[ $TITLE_START .. $TITLE_END ] ); } } { my $TYPE = 0x0143; my $GBC = 0x0080; sub gbc { my $self = shift; return $self->[$TYPE] == $GBC ? 1 : 0; } } { my $HIGH = 0x0144; my $LOW = 0x0145; sub license_code_new { my $self = shift; return $self->[$HIGH] . $self->[$LOW]; } } { my $IND = 0x0146; my $SGB = 0x0003; sub sbg { my $self = shift; return $self->[$IND] == $SGB ? 1 : 0; } } { my $IND = 0x0147; my %ROM_TYPES = ( 0x00 => 'ROM', 0x01 => 'ROM+MBC1', 0x02 => 'ROM+MBC1+RAM', 0x03 => 'ROM+MBC1+RAM+BATT', 0x05 => 'ROM+MBC2', 0x06 => 'ROM+MBC2+BATT', 0x08 => 'ROM+RAM', 0x09 => 'ROM+RAM+BATT', 0x0B => 'ROM+MMM01', 0x0C => 'ROM+MMM01+SRAM', 0x0D => 'ROM+MMM01+SRAM+BATT', 0x0F => 'ROM+MBC3+TIMER+BATT', 0x10 => 'ROM+MBC3+TIMER+RAM+BATT', 0x11 => 'ROM+MBC3', 0x12 => 'ROM+MBC3+RAM', 0x13 => 'ROM+MBC3+RAM+BATT', 0x19 => 'ROM+MBC5', 0x1A => 'ROM+MBC5+RAM', 0x1B => 'ROM+MBC5+RAM+BATT', 0x1C => 'ROM+MBC5+RUMBLE', 0x1D => 'ROM+MBC5+RUMBLE+SRAM', 0x1E => 'ROM+MBC5+RUMBLE+SRAM+BATT', 0x1F => 'Pocket Camera', 0xFD => 'Bandai TAMA5', 0xFE => 'Hudson HuC-3', 0xFF => 'Hudson HuC-1', ); sub cart_type { my $self = shift; return $ROM_TYPES{ $self->[$IND] }; } } { my $IND = 0x0148; my %SIZE = ( 0 => (32 * 1024), 1 => (64 * 1024), 2 => (128 * 1024), 3 => (256 * 1024), 4 => (512 * 1024), 5 => (1 * 1024 * 1024), 6 => (2 * 1024 * 1024), 0x52 => (1.1 * 1024 * 1024), 0x53 => (1.2 * 1024 * 1024), 0x54 => (1.5 * 1024 * 1024), ); sub rom_size { my $self = shift; return $SIZE{ $self->[$IND] }; } } { my $IND = 0x0149; my %SIZE = ( 0 => 0, 1 => 2 * 1024, 2 => 8 * 1024, 3 => 32 * 1024, 4 => 128 * 1024, ); sub ram_size { my $self = shift; return $SIZE{ $self->[$IND] }; } } { my $IND = 0x014A; sub destination_code { my $self = shift; return $self->[$IND]; } } { my $IND = 0x014B; my %LICENSESE = ( 0x33 => '', 0x79 => 'Accolade', 0xA4 => 'Konami', ); sub license_code_old { my $self = shift; return $LICENSESE{ $self->[$IND] } || ''; } } { my $IND = 0x014C; sub mask_version_number { my $self = shift; return $self->[$IND]; } } { my $IND = 0x014D; sub complement_check { my $self = shift; return $self->[$IND]; } } { my $START = 0x014E; my $END = 0x014F; sub checksum { my $self = shift; return ( $self->[$START] << 8 ) | $self->[$END]; } sub checksum_ok { my $self = shift; my @self = @$self; my @to_check = ( @self[ 0 .. $START - 1 ], @self[ $END + 1 .. $#self ], ); use List::Util 'sum'; my $checksum = 0xFFFF & sum @to_check; return $checksum == $self->checksum ? 1 : 0; } } { my $START = 0x0134; my $END = 0x014D; my $ADD = 25; sub powerup_check { my $self = shift; my @self = @$self; use List::Util 'sum'; my $check = 0xFF & ( sum ( @self[ $START .. $END ]) + $ADD ); return $check == 0 ? 1 : 0; } } package main; die "Need a cartridge to read\n" unless @ARGV; my $CART = join '', <>; sub build_array { my $data = shift; return [ unpack "C*", $data ]; } { my $cart = My::GbCart->new( build_array( $CART ) ); print "Nintendo Graphic: " . $cart->nintendo_graphic; print "Title: " . $cart->title; print "GameBoy Color: " . ($cart->gbc ? "Yes" : "No"); print "Licensee Code (old): " . ($cart->license_code_old eq '' ? "Check new license code" : $cart->license_code_old); print "Licensee Code (new): " . $cart->license_code_new; print "Super GameBoy: " . ($cart->sbg ? "Yes" : "No"); print "Cartridge Type: " . $cart->cart_type; print "ROM Size (bytes): " . $cart->rom_size; print "RAM Size (bytes): " . $cart->ram_size; print "Destination Code: " . ($cart->destination_code ? "Japanese" : "Non-Japanese"); print "Mask: " . $cart->mask_version_number; print "Complement Check: " . $cart->complement_check; print "Checksum: " . $cart->checksum . ' (' . ( $cart->checksum_ok ? 'OK' : 'NOT OK') . ')'; print "Powerup Check: " . ( $cart->powerup_check ? 'OK' : 'NOT OK' ); }