Created
November 29, 2021 06:40
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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