Skip to content

Instantly share code, notes, and snippets.

@sinsinpub
Created November 29, 2021 06:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sinsinpub/1a5e0d7fd46a51a4af9688a08aa58dba to your computer and use it in GitHub Desktop.
Save sinsinpub/1a5e0d7fd46a51a4af9688a08aa58dba to your computer and use it in GitHub Desktop.
Yet another converter of NESticle raw patch <-> Game Genie code (source from FCEU)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Game Genie: Code Mapping
*
* 6 Letters:
* | 1 | 2 | 3 | 4 | 5 | 6 |
* |1678|H234|#IJK|LABC|DMNO|5EFG|
* Value bits: 12345678
* Address bits: ABCDEFGHJKLMNO
* Length bit: # <- determines if 6 (0) or 8 (1) letters
*
* 8 Letters:
* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
* |1678|H234|#IJK|LABC|DMNO|eEFG|afgh|5bcd|
* Value bits: 12345678
* Address bits: ABCDEFGHJKLMNO
* Compare bits: abcdefgh
* Length bit: # <- determines if 6 (0) or 8 (1) letters
*/
// Letters: Group 1
#define A 0x0 // 0000
#define P 0x1 // 0001
#define Z 0x2 // 0010
#define L 0x3 // 0011
#define G 0x4 // 0100
#define I 0x5 // 0101
#define T 0x6 // 0110
#define Y 0x7 // 0111
// Letters: Group 2
#define E 0x8 // 1000
#define O 0x9 // 1001
#define X 0xA // 1010
#define U 0xB // 1011
#define K 0xC // 1100
#define S 0xD // 1101
#define V 0xE // 1110
#define N 0xF // 1111
#define DEBUGBITS 1
#define ADDR_LEN 15
#define VAL_LEN 8
#define CMP_LEN 8
#define CONVERSION_SUCCESS 0
#define ERROR_INVALID_LEN -1
#define ERROR_INVALID_CHAR -2
#define ERROR_INVALID_VAL -4
typedef unsigned int uint;
typedef unsigned short ushort;
ushort convertLetter(char letter) {
switch (letter) {
case 'A':
case 'a':
return A;
break;
case 'P':
case 'p':
return P;
break;
case 'Z':
case 'z':
return Z;
break;
case 'L':
case 'l':
return L;
break;
case 'G':
case 'g':
return G;
break;
case 'I':
case 'i':
return I;
break;
case 'T':
case 't':
return T;
break;
case 'Y':
case 'y':
return Y;
break;
case 'E':
case 'e':
return E;
break;
case 'O':
case 'o':
return O;
break;
case 'X':
case 'x':
return X;
break;
case 'U':
case 'u':
return U;
break;
case 'K':
case 'k':
return K;
break;
case 'S':
case 's':
return S;
break;
case 'V':
case 'v':
return V;
break;
case 'N':
case 'n':
return N;
break;
default:
return ERROR_INVALID_CHAR;
}
}
struct Code {
ushort addr;
ushort val;
ushort cmp;
ushort len;
};
static const char *GameGenieLetters = "APZLGITYEOXUKSVN";
int encode(struct Code * codeIn, char * codeOut) {
ushort num[8];
ushort a = codeIn->addr;
ushort v = codeIn->val;
ushort c = codeIn->cmp;
if (codeIn->len != 6 && codeIn->len != 8) {
printf("ERROR: Length %d is invalid\n", codeIn->len);
return ERROR_INVALID_LEN;
}
if (a > 0x7fff) {
printf("ERROR: Range of address $%x is invalid\n", a);
return ERROR_INVALID_VAL;
}
if (v > 0xff) {
printf("ERROR: Value $%x is invalid\n", v);
return ERROR_INVALID_VAL;
}
if (c > 0xff) {
printf("ERROR: Value $%x is invalid\n", c);
return ERROR_INVALID_VAL;
}
a &= 0x7fff;
num[0] = (v & 7) + ((v>>4) & 8);
num[1] = ((v>>4) & 7) + ((a>>4) & 8);
num[2] = ((a>>4) & 7);
num[3] = (a>>12 ) + (a & 8);
num[4] = (a & 7) + ((a>>8) & 8);
num[5] = ((a>>8) & 7);
if (codeIn->len == 6){
num[5] += v & 8;
for(int i = 0; i < 6; i++) codeOut[i] = GameGenieLetters[num[i]];
codeOut[6] = 0;
} else {
num[2] += 8;
num[5] += c & 8;
num[6] = (c & 7) + ((c>>4) & 8);
num[7] = ((c>>4) & 7) + (v & 8);
for(int i = 0; i < 8; i++) codeOut[i] = GameGenieLetters[num[i]];
codeOut[8] = 0;
}
return CONVERSION_SUCCESS;
}
#define LENGTH6 ADDR_LEN + VAL_LEN + 1
#define LENGTH8 ADDR_LEN + VAL_LEN + CMP_LEN + 1
int decode(char * codeIn, uint length, struct Code * codeOut) {
if (length != 6 && length != 8) {
printf("ERROR: Length %d is invalid\n", length);
return ERROR_INVALID_LEN;
}
ushort len = 0;
ushort addr[ADDR_LEN];
ushort val[VAL_LEN];
ushort cmp[CMP_LEN];
ushort * maps6[LENGTH6] = {
val + 0, val + 5, val + 6, val + 7,
addr + 7, val + 1, val + 2, val + 3,
&len, addr + 8, addr + 9, addr + 10,
addr + 11, addr + 0, addr + 1, addr + 2,
addr + 3, addr + 12, addr + 13, addr + 14,
val + 4, addr + 4, addr + 5, addr + 6,
};
ushort * maps8[LENGTH8] = {
val + 0, val + 5, val + 6, val + 7,
addr + 7, val + 1, val + 2, val + 3,
&len, addr + 8, addr + 9, addr + 10,
addr + 11, addr + 0, addr + 1, addr + 2,
addr + 3, addr + 12, addr + 13, addr + 14,
cmp + 4, addr + 4, addr + 5, addr + 6,
cmp + 0, cmp + 5, cmp + 6, cmp + 7,
val + 4, cmp + 1, cmp + 2, cmp + 3,
};
ushort ** maps;
if (length == 6) {
maps = maps6;
codeOut->len = 6;
} else /* length == 8 */ {
maps = maps8;
codeOut->len = 8;
}
for (int l = 0; l < length; l++) {
char letter = codeIn[l];
ushort nib = convertLetter(letter);
if (nib == ERROR_INVALID_CHAR) {
printf("ERROR: Invalid character \"%c\"\n", letter);
return ERROR_INVALID_CHAR;
}
uint iOff = l * 4;
*maps[iOff + 0] = (nib >> 3) & 1;
*maps[iOff + 1] = (nib >> 2) & 1;
*maps[iOff + 2] = (nib >> 1) & 1;
*maps[iOff + 3] = (nib >> 0) & 1;
}
len = (len == 0) ? 6 : 8;
if (len != length) {
printf("ERROR: Lengths %d and %d do not match\n", len, length);
return ERROR_INVALID_LEN;
}
// initialize output code
codeOut->addr = 0;
codeOut->val = 0;
codeOut->cmp = 0;
// compile the addr, val and cmp codes into integers
// first, compile the address
for (int a = ADDR_LEN - 1; a >= 0; a--) {
ushort bitNum = ADDR_LEN - 1 - a;
codeOut->addr |= (addr[a] & 1) << bitNum;
}
// next, the value
for (int v = VAL_LEN - 1; v >= 0; v--) {
ushort bitNum = VAL_LEN - 1 - v;
codeOut->val |= (val[v] & 1) << bitNum;
}
// lastly, the compare value
if (length == 8) {
for (int c = CMP_LEN - 1; c >= 0; c--) {
ushort bitNum = CMP_LEN - 1 - c;
codeOut->cmp |= (cmp[c] & 1) << bitNum;
}
}
return CONVERSION_SUCCESS;
}
void printBinary(ushort value) {
for (int v = sizeof(ushort) * 8 - 1; v >= 0; v--) {
ushort bit = (value >> v) & 1;
printf("%x", bit);
if (v % 4 == 0) {
printf(" ");
}
}
}
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
int printEncoded(int argc, char ** argv) {
struct Code codeIn;
char * endptr;
codeIn.addr = (ushort) strtoul(argv[1], &endptr, 16);
codeIn.val = (ushort) strtoul(argv[2], &endptr, 16);
codeIn.len = 6;
if (argc > 3) {
codeIn.len = 8;
codeIn.cmp = (ushort) strtoul(argv[3], &endptr, 16);
} else {
codeIn.cmp = 0;
}
#ifdef DEBUGBITS
printf("Length: ");
printBinary(codeIn.len);
printf("\n");
printf("Address: ");
printBinary(codeIn.addr);
printf("\n");
printf("Value: ");
printBinary(codeIn.val);
printf("\n");
printf("Compare: ");
printBinary(codeIn.cmp);
printf("\n");
#endif
char codeOut[9];
int status = encode(&codeIn, codeOut);
if (status != CONVERSION_SUCCESS) {
return EXIT_FAILURE;
}
if (codeIn.len == 6) {
printf(" %04x?%02x %s\n", codeIn.addr, codeIn.val, codeOut);
} else {
printf(" %04x?%02x:%02x %s\n", codeIn.addr, codeIn.cmp, codeIn.val, codeOut);
}
return EXIT_SUCCESS;
}
int printDecoded(char * codeIn) {
struct Code codeOut;
int status = decode(codeIn, strlen(codeIn), &codeOut);
if (status != CONVERSION_SUCCESS) {
return EXIT_FAILURE;
}
ushort len = codeOut.len;
ushort addr = codeOut.addr;
ushort val = codeOut.val;
ushort cmp = codeOut.cmp;
#ifdef DEBUGBITS
printf("Length: ");
printBinary(len);
printf("\n");
printf("Address: ");
printBinary(addr);
printf("\n");
printf("Value: ");
printBinary(val);
printf("\n");
printf("Compare: ");
printBinary(cmp);
printf("\n");
#endif
if (len == 6) {
printf(" %04x?%02x %s\n", addr, val, strupr(codeIn));
} else /* len == 8 */ {
printf(" %04x?%02x:%02x %s\n", addr, cmp, val, strupr(codeIn));
}
return EXIT_SUCCESS;
}
int main(int argc, char ** argv) {
if (argc > 2) {
return printEncoded(argc, argv);
} if (argc > 1) {
return printDecoded(argv[1]);
} else {
puts("Usage: 6/8 GG letters OR $addr(~7FFF) $val [$cmp]");
return printDecoded("SLXPLOVS");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment