Created
November 17, 2016 00:30
-
-
Save Fordi/464bf4e61b346bc5be20b07a85807878 to your computer and use it in GitHub Desktop.
Game Genie code conversion library (ported from uggconv.c)
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
var ConsoleCode = (function () { | |
var fmt = function (n, b, p) { | |
if (n === null || n === undefined) { return ''; } | |
if (isNaN(n)) { return ''; } | |
n = n.toString(b).toUpperCase(); | |
while (p > n.length) { | |
n = '0' + n; | |
} | |
return n; | |
}; | |
function GameBoy(address, value, check, description) { | |
if (!(this instanceof GameBoy)) { | |
throw new Error("GameBoy is a constructor, not a function"); | |
} | |
if (arguments.length >= 3 && !isNaN(value) && !isNaN(address)) { | |
this.address = address; | |
this.value = value; | |
if (isNaN(check)) { | |
this.description = check; | |
} else { | |
this.check = check; | |
this.description = description; | |
} | |
return; | |
} | |
this.description = value; | |
address = String(address); | |
var isGG = address.match(/^([a-f0-9]{3})-([a-f0-9]{2}[a-f9])(?:-([a-f0-9]{3}))?$/i); | |
var isRaw = address.match(/^([a-f0-9]{4}):([a-f0-9]{2})(?::([a-f0-9]{2}))?$/i); | |
if (isRaw) { | |
this.address = parseInt(isRaw[1], 16); | |
this.value = parseInt(isRaw[2], 16); | |
if (isRaw[2]) { | |
this.check = parseInt(isRaw[3], 16); | |
} | |
return; | |
} | |
if (isGG) { | |
this.value = parseInt(address[0] + address[1], 16) & 0xff; | |
this.address = parseInt(address[2] + address[4] + address[5], 16) | ((~parseInt(address[6], 16) & 0xf) << 12); | |
if (isGG[3]) { | |
this.check = (~parseInt(address[8] + address[10], 16)) & 0xff; | |
this.check = (((this.check & 0xfc) >> 2) | ((this.check & 0x03) << 6)) ^ 0x45; | |
} | |
return; | |
} | |
throw new Error([ | |
"The constructor for " + this.name + " accepts the following signatures:", | |
this.name + "(String gameGenieCode ('XXX-XXX' or 'XXX-XXX-XXX'), [String description])", | |
this.name + "(String rawCode ('AAAA:VV' or 'AAAA:VV:CC', [String description])", | |
this.name + "(Number address, Number value, [Number check], [String description])" | |
].join("\n - ")); | |
} | |
GameBoy.prototype = { | |
name: "GameBoy", | |
constructor: GameBoy, | |
toGameGenie: function () { | |
var addr = fmt(this.address, 16, 4); | |
var value = fmt(this.value, 16, 2); | |
var out = [ | |
value[0] + value[1] + addr[1], | |
addr[2] + addr[3] + (~parseInt(addr[0]) & 0xf).toString(16) | |
]; | |
if (this.check !== undefined) { | |
var ch = this.check ^ 0x45; | |
ch = ~(((ch & 0xc0) >> 6) | ((ch & 0x3f) << 2)); | |
var i = (ch & 0xf0) >> 4; | |
out.push((i & 0xf).toString(16) + ((i ^ 8) & 0xf).toString(16) + (ch & 0xf).toString(16)); | |
} | |
return out.join('-').toUpperCase(); | |
}, | |
toHigan: function () { | |
return this.toRaw().replace(/:/g, '/'); | |
}, | |
toRaw: function () { | |
var out = [ | |
fmt(this.address, 16, 4), | |
fmt(this.value, 16, 2) | |
]; | |
if (this.check != undefined) { | |
out.push(fmt(this.check, 16, 2)); | |
} | |
return out.join(':'); | |
} | |
}; | |
function Megadrive(address, value, description) { | |
if (!(this instanceof Megadrive)) { | |
throw new Error("Megadrive is a constructor, not a function"); | |
} | |
if (arguments.length >= 2 && !isNaN(value) && !isNaN(address)) { | |
this.address = address; | |
this.value = value; | |
this.description = description; | |
return; | |
} | |
address = String(address); | |
this.description = value; | |
var isGG = address.match(/^([A-Z0-9]{4})-([A-Z0-9]{4})$/i); | |
var isRaw = address.match(/^([A-F0-9]{6}):([A-F0-9]{4})$/i); | |
if (isRaw) { | |
this.address = parseInt(isRaw[1]); | |
this.value = parseInt(isRaw[2]); | |
return; | |
} | |
if (isGG) { | |
var data = (isGG[1] + isGG[2]).split('').map(function (v) { return this.constructor.chars.decode[v]; }, this); | |
this.address = ((data[3] & 0x0f) << 20) | | |
((data[4] & 0x1e) << 15) | | |
((data[1] & 0x03) << 14) | | |
((data[2] & 0x1f) << 9) | | |
((data[3] & 0x10) << 4) | | |
((data[6] & 0x07) << 5) | | |
(data[7] & 0x1f); | |
this.value = ((data[5] & 0x01) << 15) | | |
((data[6] & 0x18) << 10) | | |
((data[4] & 0x01) << 12) | | |
((data[5] & 0x1e) << 7) | | |
((data[0] & 0x1f) << 3) | | |
((data[1] & 0x16) >> 2); | |
return; | |
} | |
throw new Error([ | |
"The constructor for " + this.name + " accepts the following signatures:", | |
this.name + "(String gameGenieCode ('XXXX-XXXX'), [String description])", | |
this.name + "(String rawCode ('AAAAAA:VVVV'', [String description])", | |
this.name + "(Number address, Number value, [String description])" | |
].join("\n - ")); | |
} | |
Megadrive.prototype = { | |
name: "Megadrive", | |
constructor: Megadrive, | |
toRaw: function () { | |
return [ fmt(this.address, 16, 6), fmt(this.value, 16, 4)].join(':'); | |
}, | |
toHigan: function () { | |
return this.toRaw().replace(/:/g, '/'); | |
}, | |
toGameGenie: function () { | |
var data = [ 0, 0, 0, 0, 0, 0, 0, 0 ]; | |
data[3] |= (this.address >> 20) & 0x0f; | |
data[4] |= (this.address >> 15) & 0x1e; | |
data[1] |= (this.address >> 14) & 0x03; | |
data[2] |= (this.address >> 9) & 0x1f; | |
data[3] |= (this.address >> 4) & 0x10; | |
data[6] |= (this.address >> 5) & 0x07; | |
data[7] |= (this.address & 0x1f); | |
data[5] |= (this.value >> 15) & 0x01; | |
data[6] |= (this.value >> 10) & 0x18; | |
data[4] |= (this.value >> 12) & 0x01; | |
data[5] |= (this.value >> 7) & 0x1e; | |
data[0] |= (this.value >> 3) & 0x1f; | |
data[1] |= (this.value << 2) & 0x16; | |
data = data.map(function (i) { return this.constructor.chars.encode[i]; }, this); | |
return [ data.slice(0, 4).join(''), data.slice(4).join('') ].join('-'); | |
} | |
} | |
Megadrive.chars = {}; | |
Megadrive.chars.encode = 'ABCDEFGHJKLMNPRSTVWXYZ0123456789'.split(''); | |
Megadrive.chars.decode = Megadrive.chars.encode.reduce(function (o, ch, idx) { | |
o[ch] = idx; | |
o[ch.toLowerCase()] = idx; | |
return o; | |
}, {}); | |
function NES(address, value, check, description) { | |
if (!(this instanceof NES)) { | |
throw new Error("NES is a constructor, not a function"); | |
} | |
if (arguments.length >= 3 && !isNaN(value) && !isNaN(address)) { | |
this.address = address; | |
this.value = value; | |
if (isNaN(check)) { | |
this.description = check; | |
} else { | |
this.check = check; | |
this.description = description; | |
} | |
return; | |
} | |
address = String(address); | |
this.description = value; | |
var isGG = /^[APZLGITYEOXUKSVN]{6,8}/.test(address); | |
var isRaw = address.match(/^([a-f0-9]{4}):([a-f0-9]{2})(?::([a-f0-9]{2}))?$/i); | |
if (isRaw) { | |
this.address = parseInt(isRaw[1], 16); | |
this.value = parseInt(isRaw[2], 16); | |
if (isRaw[2]) { | |
this.check = parseInt(isRaw[3], 16); | |
} | |
return; | |
} else if (isGG) { | |
var data = address.split('').map(function (v) { return this.constructor.chars.decode[v]; }, this); | |
this.address = 0x8000 | | |
((data[1] & 8) << 4) | | |
((data[2] & 7) << 4) | | |
((data[3] & 7) << 12) | | |
((data[3] & 8) << 0) | | |
((data[4] & 7) << 0) | | |
((data[4] & 8) << 8) | | |
((data[5] & 7) << 8); | |
if (address.length === 8) { | |
this.value = ((data[0] & 7) << 0) | | |
((data[0] & 8) << 4) | | |
((data[1] & 7) << 4) | | |
((data[7] & 8) << 0); | |
this.check = ((data[6] & 7) << 0) | | |
((data[6] & 8) << 0) | | |
((data[6] & 8) << 4) | | |
((data[7] & 7) << 4); | |
} else { | |
this.value = ((data[0] & 7) << 0) | | |
((data[0] & 8) << 4) | | |
((data[1] & 7) << 4) | | |
((data[5] & 8) << 0); | |
} | |
return; | |
} | |
throw new Error([ | |
"The constructor for " + this.name + " accepts the following signatures:", | |
this.name + "(String gameGenieCode ('XXXXXX' or 'XXXXXXXX'), [String description])", | |
this.name + "(String rawCode ('AAAA:VV' or 'AAAA:VV:CC', [String description])", | |
this.name + "(Number address, Number value, [Number check], [String description])" | |
].join("\n - ")); | |
} | |
NES.prototype = { | |
name: "NES", | |
constructor: NES, | |
toRaw: function () { | |
var out = [ | |
fmt(this.address, 16, 4), | |
fmt(this.value, 16, 2) | |
]; | |
if (this.check != undefined) { | |
out.push(fmt(this.check, 16, 2)); | |
} | |
return out.join(':'); | |
}, | |
toHigan: function () { | |
return this.toRaw().replace(/:/g, '/'); | |
}, | |
toGameGenie: function () { | |
var data = isNaN(this.check) ? [0, 0, 0, 0, 0, 0] : [0, 0, 0, 0, 0, 0, 0, 0]; | |
var address = this.address & 0x7fff; | |
//encodeNES(v, n, m, s) data[n] |= (v >> s) & m | |
data[0] = ((this.value >> 0) & 7) | | |
((this.value >> 4) & 8); | |
data[1] = ((this.address >> 4) & 8) | | |
((this.value >> 4) & 7); | |
data[2] = (this.address >> 4) & 7; | |
data[3] = ((address >> 12) & 7) | | |
((address >> 0) & 8); | |
data[4] = ((address >> 0) & 7) | | |
((address >> 8) & 8); | |
data[5] = ((this.address >> 8) & 7) | | |
// Differs from original uggconv: | |
// placing this outside the `no-check` enables truncated with-check code to operate unchecked by dropping last two chars | |
((this.value >> 0) & 8); | |
if (!isNaN(this.check)) { | |
data[6] = ((this.check >> 0) & 7) | | |
((this.check >> 0) & 8) | | |
((this.check >> 4) & 8); | |
data[7] = ((this.check >> 4) & 7) | | |
((this.value >> 0) & 8); | |
} | |
return data.map(function (v) { return this.constructor.chars.encode[v]; }, this).join(''); | |
} | |
}; | |
NES.chars = {}; | |
NES.chars.encode = 'APZLGITYEOXUKSVN'.split(''); | |
NES.chars.decode = NES.chars.encode.reduce(function (o, ch, idx) { | |
o[ch] = idx; | |
o[ch.toLowerCase()] = idx; | |
return o; | |
}, {}); | |
function SuperNES(address, value, description) { | |
if (!(this instanceof SuperNES)) { | |
throw new Error("SuperNES is a constructor, not a function"); | |
} | |
if (arguments.length >= 2) { | |
this.address = address; | |
this.value = value; | |
this.description = description; | |
return; | |
} | |
address = String(address); | |
this.description = value; | |
var isRaw = address.match(/^([0-9a-z]{6})\:([0-9a-z]{2})$/i); | |
var isGG = address.match(/^([0-9a-z]{4})-([0-9a-z]{4})$/i); | |
if (isRaw) { | |
this.address = parseInt(isRaw[1], 16); | |
this.value = parseInt(isRaw[2], 16); | |
return; | |
} | |
if (isGG) { | |
var data = (isGG[1] + isGG[2]).split('').map(function (v) { | |
return this.constructor.chars.decode[v]; | |
}, this); | |
this.value = data[0] << 4 + data[1]; | |
var cp = (data[2] << 20) + (data[3] << 16) + (data[4] << 12) + (data[5] << 8) + (data[6] << 4) + data[7]; | |
console.log(cp); | |
this.address = (((cp & (0xc00000 >> 0)) << 0) >> 8) | | |
(((cp & (0xc00000 >> 2)) << 2) >> 10) | | |
(((cp & (0xc00000 >> 4)) << 4) >> 16) | | |
(((cp & (0xc00000 >> 6)) << 6) >> 18) | | |
(((cp & (0xc00000 >> 8)) << 8) >> 14) | | |
(((cp & (0xc00000 >> 10)) << 10) >> 0) | | |
(((cp & (0xc00000 >> 12)) << 12) >> 2) | | |
(((cp & (0xc00000 >> 14)) << 14) >> 20) | | |
(((cp & (0xc00000 >> 16)) << 16) >> 22) | | |
(((cp & (0xc00000 >> 18)) << 18) >> 4) | | |
(((cp & (0xc00000 >> 20)) << 20) >> 6) | | |
(((cp & (0xc00000 >> 22)) << 22) >> 12); | |
return; | |
} | |
throw new Error([ | |
"The constructor for " + this.name + " accepts the following signatures:", | |
this.name + "(String gameGenieCode ('XXXX-XXXX'), [String description])", | |
this.name + "(String rawCode ('AAAAAA:VV', [String description])", | |
this.name + "(Number address, Number value, [String description])" | |
].join("\n - ")); | |
} | |
SuperNES.prototype = { | |
name: "SuperNES", | |
constructor: SuperNES, | |
toRaw: function () { | |
return [ fmt(this.address, 16, 6), fmt(this.value, 16, 2)].join(':'); | |
}, | |
toHigan: function () { | |
return this.toRaw().replace(/:/g, '/'); | |
}, | |
toGameGenie: function () { | |
var cp = (((this.address & (0xc00000 >> 8)) << 8) >> 0) | | |
(((this.address & (0xc00000 >> 10)) << 10) >> 2) | | |
(((this.address & (0xc00000 >> 16)) << 16) >> 4) | | |
(((this.address & (0xc00000 >> 18)) << 18) >> 6) | | |
(((this.address & (0xc00000 >> 14)) << 14) >> 8) | | |
(((this.address & (0xc00000 >> 0)) << 0) >> 10) | | |
(((this.address & (0xc00000 >> 2)) << 2) >> 12) | | |
(((this.address & (0xc00000 >> 20)) << 20) >> 14) | | |
(((this.address & (0xc00000 >> 22)) << 22) >> 16) | | |
(((this.address & (0xc00000 >> 4)) << 4) >> 18) | | |
(((this.address & (0xc00000 >> 6)) << 6) >> 20) | | |
(((this.address & (0xc00000 >> 12)) << 12) >> 22); | |
return [ | |
[ | |
this.constructor.chars.encode[this.value >> 4], | |
this.constructor.chars.encode[this.value & 0xf], | |
this.constructor.chars.encode[((cp & 0xf00000) >> 20) & 0xf], | |
this.constructor.chars.encode[((cp & 0x0f0000) >> 16) & 0xf] | |
].join(''), | |
[ | |
this.constructor.chars.encode[((cp & 0x00f000) >> 12) & 0xf], | |
this.constructor.chars.encode[((cp & 0x000f00) >> 8) & 0xf], | |
this.constructor.chars.encode[((cp & 0x0000f0) >> 4) & 0xf], | |
this.constructor.chars.encode[((cp & 0x00000f) ) & 0xf] | |
].join('') | |
].join('-'); | |
} | |
} | |
SuperNES.chars = {}; | |
SuperNES.chars.encode = 'DF4709156BC8A23E'.split(''); | |
SuperNES.chars.decode = SuperNES.chars.encode.reduce(function (o, ch, idx) { | |
o[ch] = idx; | |
o[ch.toLowerCase()] = idx; | |
return o; | |
}, {}); | |
return { | |
GameBoy: GameBoy, | |
GameGear: GameBoy, | |
NES: NES, | |
SuperNES: SuperNES, | |
Megadrive: Megadrive | |
} | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment