#!/usr/bin/perl # # gg - convert a Game Genie code for the N.E.S. # Col. G. L. Sicherman (colonel@monmouth.com). 2002-07-30. # # gg # gg
[checkvalue] # # If you specify a code, prints the address, value, and comparison # value if any. Case is ignored in the argument. All output values # are in hexadecimal. # # If you specify an address, a value, and an optional comparison # value, prints the code. All arguments are in hexadecimal; # case is ignored. (So is the high bit of the address.) # # This follows the mapping defined in Benzene's document 0.71. # The algorithm is better than his, though! sub usage { die "usage:\tgg \n\tgg
[checkvalue]\n" } $ggchar = "AEPOZXLUGKISTVYN"; if (@ARGV==1) { $code = shift; &decode; # No return. } if (@ARGV==2 || @ARGV==3) { ($addr, $value, $checkvalue) = @ARGV; &encode; # No return. } &usage; sub decode { $codelen = length $code; if ($codelen != 6 && $codelen != 8) { die "gg: code must have 6 or 8 characters\n"; } $code = uc $code; for (split //, $code) { $i = index $ggchar, $_; $i >= 0 or die("gg: illegal code character " . &showchar($_) . "\n"); $bits = ($bits << 4) | $i; } # Rotate the bits 1 position rightward. $tailbit = $bits & 1; $bits = ($bits >> 1) | ($tailbit << ($codelen==8? 31: 23)); $bits <<= 8 if $codelen==6; # Pad out with zero bits. $iscond = ($bits >> 19) & 1; if ($iscond && $codelen==6) { die "gg: high bit not set for conditional code\n"; } if (!$iscond && $codelen==8) { die "gg: high bit set for unconditional code\n"; } $addr = (($bits & 0x000f0000) >> 4) | (($bits & 0x00000f00) >> 0) | (($bits & 0x00f00000) >> 16) | (($bits & 0x0000f000) >> 12) | 0x8000; $value = (($bits & 0x0f000000) >> 20) | (($bits & 0xf0000000) >> 28); printf "%04x %02x", $addr, $value; if ($codelen==8) { $checkvalue = (($bits & 0x0000000f) << 4) | (($bits & 0x000000f0) >> 4); printf " %02x", $checkvalue; } print "\n"; exit 0; } sub showchar { $ord = ord $_[0]; return $ord if 0x41 <= $ord && $ord <= 0x7e; return "SP" if 0x40 == $ord; return "^?" if 0x7f == $ord; return "^" . chr(0x40 + $ord) if $ord < 0x40; return "M-" . &showchar(chr($ord) & 0x7f); } sub encode { $addr =~ /^[0-9a-f]{1,4}$/i or &usage; $zaddr = hex $addr; $value =~ /^[0-9a-f]{1,2}$/i or &usage; $zvalue = hex $value; if (@ARGV==3) { $checkvalue =~ /^[0-9a-f]{1,2}$/i or &usage; $zcheckvalue = hex $checkvalue; $zaddr |= 0x8000; } else { $zcheckvalue = 0; $zaddr &= ~0x8000; } $bits = $zvalue & 0xf; $bits <<= 4; $bits |= ($zvalue & 0xf0) >> 4; $bits <<= 4; $bits |= ($zaddr & 0x00f0) >> 4; $bits <<= 4; $bits |= ($zaddr & 0xf000) >> 12; $bits <<= 4; $bits |= $zaddr & 0x000f; $bits <<= 4; $bits |= ($zaddr & 0x0f00) >> 8; $bits <<= 4; $bits |= $zcheckvalue & 0x0f; $bits <<= 4; $bits |= ($zcheckvalue & 0xf0) >> 4; # Rotate the bits 1 position left. $headbit = ($bits >> 31) & 1; $bits = ($bits << 1) | $headbit; for ($pos=7; $pos>=0; $pos--) { $code = substr($ggchar, $bits & 0x0000000f, 1) . $code; $bits >>= 4; } $code = substr $code, 0, 6 if @ARGV==2; print $code, "\n"; exit 0; }