Open satoshinm opened 6 years ago
http://forums.nesdev.com/viewtopic.php?f=3&t=13455&p=158623 ran into the same problem (dead link), but notes that http://www.chrismcovell.com/CheatConverter.html has working JavaScript for Pro Action Rocky encoding/decoding. Copying the complete source here so it doesn't get lost again:
/*
Because this is in javascript and javascript has trouble using bytes with the highest bit
set (try doing alert(0x80000000>>1 == 0x40000000)) I had to do things in a weird way.
You may use snippits of this code in any program you want, if it is the game genie or pro action rockey decode or encode. Include this file with the program or put a thanks to Blue Hawk and this script title.
Coded by Blue Hawk.
*/
// Bit descrambling arrays
var rocky_shifts = new Array(
3,13,14,1,6,9,5,0,12,7,2,8,10,11,4, // addr
19,21,23,22,20,17,16,18, // compare
29,31,24,26,25,30,27,28 // replace
);
//%Addr (15 bits) %Val8 compare in special var
var gg_shift = new Array(
0, 1, 2, 7,
4, 5, 6, 15,
12, 13, 14, 23,
20, 21, 22, 11,
8, 9, 10, 19,
16, 17, 18, 3
);
var hexlet = "0123456789ABCDEF";
var GG_letters = "APZLGITYEOXUKSVN";
var cmp, addr, val, ggCode;
// Everything is shifted one bit right to avoid the high bit being set
// which certain browser doesn't handle well.
var rocky_key = 0x7e5ee93a;
var rocky_xor = 0x5c184b91;
function filter()
{
var n, ggCode2, ch ;
ggCode = document.frm.ggCode.value;
ggCode = ggCode.toUpperCase();
ggCode2 = "";
for (n=0; n < ggCode.length; n++)
{
ch = ggCode.charAt(n)
if (GG_letters.indexOf(ch) != -1)
ggCode2 += ch;
}
document.frm.ggCode.value = ggCode2;
}
function toHex(val, textSize)
{
var hex = "";
for(var i=0; i < textSize; i++)
{
n = (val >> (i * 4)) & 15;
hex = hexlet.charAt(n) + hex;
}
return hex;
}
function fromHex(hexText)
{
var decVal = 0;
hexText = hexText.toUpperCase();
for(var i=0; i < hexText.length; i++)
{
decVal <<= 4;
n = hexText.charAt(i);
n = hexlet.indexOf(n);
if (n == -1)
return "bad values";
else
decVal += n;
}
return decVal;
}
function decodeGG()
{
var n, num = new Array, ch;
filter();
ggCode = document.frm.ggCode.value;
if ((ggCode.length != 6) && (ggCode.length != 8))
{
alert("Bad Game Genie size");
return;
}
for (n=0; n < ggCode.length; n++)
{
ch = ggCode.charAt(n);
num[n] = GG_letters.indexOf(ch) - 0;
}
ch = 0;
if ((num[2] & 8) && (ggCode.length == 8))
{
ggDecode(num)
ggDecode8(num);
document.frm.cmp.value = toHex(cmp, 2);
ch = 1;
}
else if (!(num[2] & 8) && (ggCode.length == 6))
{
ggDecode(num);
document.frm.cmp.value = "";
document.frm.rocky.value = "";
}
else
{
alert("Bad game genie code");
return;
}
document.frm.addr.value = toHex(addr, 4);
document.frm.val.value = toHex(val, 2);
if (ch)
paRockyEncode();
}
function encodeVal()
{
var eightLetters = 0;
addr = document.frm.addr.value;
addr = fromHex(addr);
if (addr == "bad values")
{
document.frm.addr.value = addr;
return;
}
else
{
addr |= 0x8000;
document.frm.addr.value = toHex(addr, 4);
}
val = document.frm.val.value;
val = fromHex(val);
document.frm.val.value = val;
if (val == "bad values")
{
document.frm.val.value = val;
return;
}
else
document.frm.val.value = toHex(val, 2);
cmp = document.frm.cmp.value;
if (cmp.length)
{
cmp = fromHex(cmp);
document.frm.cmp.value = cmp;
if (cmp == "bad values")
{
document.frm.cmp.value = cmp;
return;
}
else
document.frm.cmp.value = toHex(cmp, 2);
eightLetters = 1;
}
else
cmp = 0;
encodeGG(eightLetters);
if (eightLetters)
paRockyEncode();
else
document.frm.rocky.value = "";
}
function encodeGG(eightLetters)
{
var n, num = new Array;
ggEncode(num);
if (eightLetters)
{
ggEncode8(num);
num[2] |= 8;
}
else
{
ggEncode6(num);
num[2] &= 255 - 8;
}
ggCode = "";
for (n=0; n < (eightLetters ? 8 :6) ; n++)
ggCode += GG_letters.charAt(num[n]);
document.frm.ggCode.value = ggCode;
}
function ggDecode8(num)
{
val = val & (255 - 8);
val |= num[7] & 8 ? 0x08 : 0x00;
cmp = num[6] & 8 ? 0x80 : 0x00;
cmp |= num[7] & 4 ? 0x40 : 0x00;
cmp |= num[7] & 2 ? 0x20 : 0x00;
cmp |= num[7] & 1 ? 0x10 : 0x00;
cmp |= num[5] & 8 ? 0x08 : 0x00;
cmp |= num[6] & 4 ? 0x04 : 0x00;
cmp |= num[6] & 2 ? 0x02 : 0x00;
cmp |= num[6] & 1 ? 0x01 : 0x00;
}
function ggDecode(num)
{
var n, i;
val = 0;
for (n=0; n<6; n++) {
for (i=0; i<4; i++) {
if (num[n] & 1<<i)
val |= 1<<gg_shift[n*4 + i];
}
}
addr = (val>>8) | 0x8000;
val &= 0xFF;
}
function ggEncode(num)
{
var n, i;
val &= 0xFF;
addr|= 0x8000;
val |= addr<<8;
num[0] = num[1] = num[2] = num[3] = 0;
num[4] = num[5] = num[6] = num[7] = 0;
for (n=0; n<6; n++) {
for (i=0; i<4; i++) {
if (val & 1<<gg_shift[n*4 + i])
num[n] |= 1<<i;
}
}
}
function ggEncode8(num)
{
num[2] |= 8;
num[7] |= val & 0x08 ? 8 : 0;
num[6] = cmp & 0x80 ? 8 : 0;
num[7] |= cmp & 0x40 ? 4 : 0;
num[7] |= cmp & 0x20 ? 2 : 0;
num[7] |= cmp & 0x10 ? 1 : 0;
num[5] &= 255 - 8;
num[5] |= cmp & 0x08 ? 8 : 0;
num[6] |= cmp & 0x04 ? 4 : 0;
num[6] |= cmp & 0x02 ? 2 : 0;
num[6] |= cmp & 0x01 ? 1 : 0;
}
function ggEncode6(num)
{
num[5] |= val & 0x08 ? 8 : 0;
}
function paRockyDecode()
{
encoded = document.frm.rocky.value;
encoded = fromHex( encoded.substr(0, 8) );
document.frm.rocky.value = toHex( encoded, 8 );
encoded >>= 1;
key = rocky_key;
decoded = 0;
for ( i = 31; i--; )
{
if ( (key ^ encoded) & 0x40000000 ) {
decoded |= 1 << rocky_shifts [i];
key ^= rocky_xor;
}
encoded <<= 1;
key <<= 1;
}
addr = (decoded & 0x7fff) | 0x8000;
cmp = (decoded >> 16) & 0xff;
val = (decoded >> 24) & 0xff;
document.frm.addr.value = toHex( addr, 4 );
document.frm.cmp.value = toHex( cmp, 2 );
document.frm.val.value = toHex( val, 2 );
encodeGG(1);
}
function paRockyEncode()
{
decoded = fromHex(document.frm.addr.value) & 0x7fff;
decoded |= fromHex(document.frm.cmp.value) << 16;
decoded |= fromHex(document.frm.val.value) << 24;
key = rocky_key;
encoded = 0;
for ( i = 31; i--; )
{
bit = decoded >> rocky_shifts [i];
if ( ((key >> 30) ^ bit) & 1 )
encoded |= 2 << i;
if ( bit & 1 )
key ^= rocky_xor;
key <<= 1;
}
document.frm.rocky.value = toHex(encoded , 8);
}
Added in https://github.com/satoshinm/famicom-pro-action-rocky https://www.npmjs.com/package/famicom-pro-action-rocky but would be perhaps worth adding to cli and web examples of https://github.com/satoshinm/nes-game-genie/tree/master/examples
http://chrismcovell.com/fcrocky.html
Comparable to Game Genie, but the Pro Action Rocky is for Famicom, and uses a different code format. Would be nice to support, maybe as a separate module.
Unfortunately, the link from http://chrismcovell.com/fcrocky.html describing the technical details of the code format seems to be broken: http://chrismcovell.com/fcrocky_codes.txt 500 Internal Server Error, and it may not be available on the Wayback Machine either. But there are sample codes available at http://chrismcovell.com/texts/RockyDump-chris.html and new codes at http://chrismcovell.com/NewRockyCodes.htm which have corresponding decoded codes, example:
https://gamehacking.org/system/nes has a code converter, raw/gg/par