536 lines
No EOL
19 KiB
JavaScript
536 lines
No EOL
19 KiB
JavaScript
|
|
//////
|
|
let fs = require("fs")
|
|
let memoryjs = require('memoryjs')
|
|
let process_name = "RALibretro.exe"
|
|
let module_name = 'mednafen_saturn_libretro.dll'
|
|
let process = memoryjs.openProcess(process_name)
|
|
// get specific dll inside of an EXE
|
|
//let modules = memoryjs.getModules(process.th32ProcessID);
|
|
// dwSize: 304,
|
|
// th32ProcessID: 3996,
|
|
// cntThreads: 26,
|
|
// th32ParentProcessID: 9416,
|
|
// pcPriClassBase: 8,
|
|
// szExeFile: 'RALibretro.exe',
|
|
// handle: 644,
|
|
// modBaseAddr: 140699475116032
|
|
let core = memoryjs.findModule(module_name, process.th32ProcessID);
|
|
// modBaseAddr: 140714432069632,
|
|
// modBaseSize: 24608768,
|
|
// szExePath: 'F:\\Games\\Console\\RALibretro\\Cores\\mednafen_saturn_libretro.dll',
|
|
// szModule: 'mednafen_saturn_libretro.dll',
|
|
// th32ProcessID: 3996,
|
|
// GlblcntUsage: 1
|
|
|
|
let hex2int = function(hex) {
|
|
let result = parseInt(Number(hex), 10)
|
|
return result
|
|
}
|
|
|
|
let char_tbl = {
|
|
[hex2int("0x00")] : ' ',
|
|
[hex2int("0x41")] : 'A',
|
|
[hex2int("0x42")] : 'B',
|
|
[hex2int("0x43")] : 'C',
|
|
[hex2int("0x44")] : 'D',
|
|
[hex2int("0x45")] : 'E',
|
|
[hex2int("0x46")] : 'F',
|
|
[hex2int("0x47")] : 'G',
|
|
[hex2int("0x48")] : 'H',
|
|
[hex2int("0x49")] : 'I',
|
|
[hex2int("0x4a")] : 'J',
|
|
[hex2int("0x4b")] : 'K',
|
|
[hex2int("0x4c")] : 'L',
|
|
[hex2int("0x4d")] : 'M',
|
|
[hex2int("0x4e")] : 'N',
|
|
[hex2int("0x4f")] : 'O',
|
|
[hex2int("0x50")] : 'P',
|
|
[hex2int("0x51")] : 'Q',
|
|
[hex2int("0x52")] : 'R',
|
|
[hex2int("0x53")] : 'S',
|
|
[hex2int("0x54")] : 'T',
|
|
[hex2int("0x55")] : 'U',
|
|
[hex2int("0x56")] : 'V',
|
|
[hex2int("0x57")] : 'W',
|
|
[hex2int("0x58")] : 'X',
|
|
[hex2int("0x59")] : 'Y',
|
|
[hex2int("0x5a")] : 'Z',
|
|
|
|
[hex2int("0x61")] : 'a',
|
|
[hex2int("0x62")] : 'b',
|
|
[hex2int("0x63")] : 'c',
|
|
[hex2int("0x64")] : 'd',
|
|
[hex2int("0x65")] : 'e',
|
|
[hex2int("0x66")] : 'f',
|
|
[hex2int("0x67")] : 'g',
|
|
[hex2int("0x68")] : 'h',
|
|
[hex2int("0x69")] : 'i',
|
|
[hex2int("0x6a")] : 'j',
|
|
[hex2int("0x6b")] : 'k',
|
|
[hex2int("0x6c")] : 'l',
|
|
[hex2int("0x6d")] : 'm',
|
|
[hex2int("0x6e")] : 'n',
|
|
[hex2int("0x6f")] : 'o',
|
|
[hex2int("0x70")] : 'p',
|
|
[hex2int("0x71")] : 'q',
|
|
[hex2int("0x72")] : 'r',
|
|
[hex2int("0x73")] : 's',
|
|
[hex2int("0x74")] : 't',
|
|
[hex2int("0x75")] : 'u',
|
|
[hex2int("0x76")] : 'v',
|
|
[hex2int("0x77")] : 'w',
|
|
[hex2int("0x78")] : 'x',
|
|
[hex2int("0x79")] : 'y',
|
|
[hex2int("0x7a")] : 'z',
|
|
|
|
[hex2int("0x21")] : '!',
|
|
[hex2int("0x2e")] : '.',
|
|
[hex2int("0x2f")] : '/',
|
|
[hex2int("0x26")] : '&',
|
|
[hex2int("0x2b")] : '+',
|
|
[hex2int("0x2c")] : ',',
|
|
[hex2int("0x30")] : '0',
|
|
[hex2int("0x31")] : '1',
|
|
[hex2int("0x32")] : '2',
|
|
[hex2int("0x33")] : '3',
|
|
[hex2int("0x34")] : '4',
|
|
[hex2int("0x35")] : '5',
|
|
[hex2int("0x36")] : '6',
|
|
[hex2int("0x37")] : '7',
|
|
[hex2int("0x38")] : '8',
|
|
[hex2int("0x39")] : '9',
|
|
[hex2int("0x3a")] : ':',
|
|
[hex2int("0x3b")] : ';',
|
|
[hex2int("0x3f")] : '?',
|
|
[hex2int("0xb0")] : '-',
|
|
}
|
|
|
|
let tint2char = function(int) {
|
|
return char_tbl[int]
|
|
}
|
|
|
|
// CE gives module name + offset (hex)
|
|
let offsets = {
|
|
discrepancy : hex2int("0x77A340"), // 7840576
|
|
map_id : hex2int("0x18006"),
|
|
event_id : hex2int("0x1cf8ac"),
|
|
eid : hex2int("0x1cf8b6"),
|
|
ingame_indicator : hex2int("0x121510"),
|
|
savefile_name : [
|
|
hex2int("0x120731"),
|
|
hex2int("0x120730"),
|
|
hex2int("0x120733"),
|
|
hex2int("0x120732"),
|
|
hex2int("0x120735"),
|
|
hex2int("0x120734"),
|
|
],
|
|
hp : {
|
|
arthur : hex2int("0x1207b0"),
|
|
melody : hex2int("0x12087c"),
|
|
rodi : hex2int("0x120948"),
|
|
basso : hex2int("0x120a14"),
|
|
akane : hex2int("0x120ae0"),
|
|
forte : hex2int("0x120bac"),
|
|
doyle : hex2int("0x120c78"),
|
|
lisa : hex2int("0x120d44"),
|
|
enemy1 : hex2int("0x120e10"),
|
|
enemy2 : hex2int("0x120edc"),
|
|
enemy3 : hex2int("0x120fa8"),
|
|
enemy4 : hex2int("0x121074"),
|
|
enemy5 : hex2int("0x121140"),
|
|
},
|
|
max_hp : {
|
|
arthur : hex2int("0x12073c"),
|
|
melody : hex2int("0x120808"),
|
|
rodi : hex2int("0x1208d4"),
|
|
basso : hex2int("0x1209a0"),
|
|
akane : hex2int("0x120a6c"),
|
|
forte : hex2int("0x120b38"),
|
|
doyle : hex2int("0x120c04"),
|
|
lisa : hex2int("0x120cd0"),
|
|
enemy1 : hex2int("0x120d9c"),
|
|
enemy2 : hex2int("0x120f34"),
|
|
},
|
|
mp : {
|
|
arthur : hex2int("0x1207b2"),
|
|
melody : hex2int("0x12087e"),
|
|
rodi : hex2int("0x12094a"),
|
|
basso : hex2int("0x120a16"),
|
|
akane : hex2int("0x120ae2"),
|
|
forte : hex2int("0x120bae"),
|
|
lisa : hex2int("0x120d46"),
|
|
enemy1 : hex2int("0x120e12"),
|
|
enemy2 : hex2int("0x120ede"),
|
|
},
|
|
max_mp : {
|
|
arthur : hex2int("0x12073e"),
|
|
melody : hex2int("0x12080a"),
|
|
rodi : hex2int("0x1208d6"),
|
|
basso : hex2int("0x1209a2"),
|
|
akane : hex2int("0x120a6e"),
|
|
forte : hex2int("0x120b3a"),
|
|
lisa : hex2int("0x120cd2"),
|
|
enemy1 : hex2int("0x120d9e"),
|
|
enemy2 : hex2int("0x120e2a"),
|
|
},
|
|
status : {
|
|
arthur : hex2int("0x1207eb"),
|
|
melody : hex2int("0x1208b7"),
|
|
rodi : hex2int("0x120983"),
|
|
basso : hex2int("0x120a4f"),
|
|
akane : hex2int("0x120b1b"),
|
|
forte : hex2int("0x120be7"),
|
|
doyle : hex2int("0x120cb3"),
|
|
lisa : hex2int("0x120d7f"),
|
|
},
|
|
position : {
|
|
x: hex2int("0x1219dc"),
|
|
y: hex2int("0x1219e4"),
|
|
z: hex2int("0x1219e1"),
|
|
z2: hex2int("0x1219e0"),
|
|
north: hex2int("0x1ffc54"),
|
|
west: hex2int("0x1ffc58"),
|
|
direction: hex2int("0x121a0c")
|
|
},
|
|
steps : {
|
|
area: hex2int("0x121512")
|
|
},
|
|
characters: {
|
|
party:hex2int("0x121501"),
|
|
acquired:hex2int("0x121503"),
|
|
},
|
|
pixies : {
|
|
0 : hex2int("0x1215d8"),
|
|
1 : hex2int("0x1215da"),
|
|
2 : hex2int("0x1215db"),
|
|
3 : hex2int("0x1215dc"),
|
|
4 : hex2int("0x1215dd"),
|
|
5 : hex2int("0x1215de"),
|
|
6 : hex2int("0x1215df"),
|
|
},
|
|
|
|
}
|
|
|
|
|
|
function readm(offset, type) {
|
|
return memoryjs.readMemory(process.handle, core.modBaseAddr+offsets.discrepancy+offset, type || memoryjs.UINT16)
|
|
}
|
|
|
|
// use this to send data only *1 in a throttle* readings
|
|
let throttle = 1
|
|
let throttle_counter = 0
|
|
let last_map_id
|
|
let mapped_data = {}
|
|
let maps = {}
|
|
let last_pos = {}
|
|
let reset_cell_size = true
|
|
let reset_cell_size_sent = false
|
|
let last_ingame
|
|
let last_worldmap
|
|
let WSS
|
|
|
|
if (fs.existsSync('./maps.json')) {
|
|
maps = require ('./maps.json')
|
|
}
|
|
|
|
function new_ws_server(port) {
|
|
let WS = require('ws')
|
|
let lcrypto = require('crypto')
|
|
WSS = new WS.Server({port:port})
|
|
console.log('listening on ws://'+ 'localhost' +":"+ port)
|
|
WSS.send = function (client_id, message) {
|
|
|
|
let json_data = JSON.stringify(message)
|
|
for (let WSC of WSS.clients) {
|
|
if (WSC.id == client_id || client_id == "broadcast" || client_id == WSC.client_channel ) {
|
|
WSC.send(json_data)
|
|
}
|
|
}
|
|
}
|
|
|
|
function WSSconnect(ws_client) {
|
|
console.log('[WSS] A new client has appeared!')
|
|
ws_client.join_time = new Date()
|
|
ws_client.on('pong', function() {
|
|
// this will re-animate a "dead" client so WSS_dead_clients_check won't kill it
|
|
this.alive = true
|
|
})
|
|
|
|
}
|
|
|
|
WSS.on('connection', WSSconnect)
|
|
|
|
|
|
setInterval(
|
|
function() {
|
|
let data = {
|
|
hp : {},
|
|
max_hp : {},
|
|
mp : {},
|
|
max_mp : {},
|
|
status : {},
|
|
}
|
|
|
|
// pixies ---------------------------------------------------------------
|
|
let pixies = []
|
|
for (let idx in offsets.pixies) {
|
|
let batch = (readm(offsets.pixies[idx], memoryjs.UBYTE)).toString(2)
|
|
pixies[idx] = ('0'.repeat(8-batch.length) + batch).split("").reverse().join("")
|
|
}
|
|
|
|
data.pixies = pixies.join("").split("")
|
|
|
|
data.pixies_flags = {
|
|
leprechaunt : {
|
|
Dana : data.pixies[54],
|
|
Tak : data.pixies[55],
|
|
Morgan : data.pixies[40],
|
|
Kokus : data.pixies[41],
|
|
Zircon : data.pixies[42],
|
|
Mangus : data.pixies[43],
|
|
Darbie : data.pixies[44],
|
|
Solo : data.pixies[45],
|
|
Stilt : data.pixies[46],
|
|
Eric : data.pixies[47],
|
|
},
|
|
succubus : {
|
|
Muran : data.pixies[34],
|
|
Dahlia : data.pixies[35],
|
|
Roberia : data.pixies[36],
|
|
Lacey : data.pixies[37],
|
|
Orlea : data.pixies[38],
|
|
Ripanos : data.pixies[39],
|
|
Kathorea : data.pixies[24],
|
|
Viola : data.pixies[25],
|
|
Lunaria : data.pixies[26],
|
|
Natasha : data.pixies[27],
|
|
},
|
|
incubus : {
|
|
Lantano : data.pixies[28],
|
|
Enjewel : data.pixies[29],
|
|
Masakari : data.pixies[30],
|
|
Krupis : data.pixies[31],
|
|
Liknis : data.pixies[48],
|
|
Cypress : data.pixies[49],
|
|
Aster : data.pixies[50],
|
|
Adonis : data.pixies[51],
|
|
Croton : data.pixies[52],
|
|
Boris : data.pixies[53],
|
|
},
|
|
pixie : {
|
|
Maple : data.pixies[6],
|
|
Cherry : data.pixies[7],
|
|
Willow : data.pixies[16],
|
|
Cedar : data.pixies[17],
|
|
Palm : data.pixies[18],
|
|
Apple : data.pixies[19],
|
|
Lime : data.pixies[20],
|
|
Pear : data.pixies[21],
|
|
Plum : data.pixies[22],
|
|
Baldric : data.pixies[23],
|
|
},
|
|
fairy : {
|
|
Daisy : data.pixies[8],
|
|
Iris : data.pixies[9],
|
|
Camellia : data.pixies[10],
|
|
Peony : data.pixies[11],
|
|
Lily : data.pixies[12],
|
|
Azalea : data.pixies[13],
|
|
Sisal : data.pixies[14],
|
|
Mimosa : data.pixies[15],
|
|
Primrose : data.pixies[32],
|
|
Clyde : data.pixies[33],
|
|
},
|
|
}
|
|
// ------ ---------------------------------------------------------------
|
|
|
|
data.characters = readm(offsets.characters.acquired).toString(2)
|
|
data.party = readm(offsets.characters.party).toString(2)
|
|
|
|
let acquired_characters = {}
|
|
if (data.characters[0]==1) { acquired_characters.arthur = true }
|
|
if (data.characters[1]==1) { acquired_characters.melody = true }
|
|
if (data.characters[2]==1) { acquired_characters.rodi = true }
|
|
if (data.characters[3]==1) { acquired_characters.basso = true }
|
|
if (data.characters[4]==1) { acquired_characters.akane = true }
|
|
if (data.characters[5]==1) { acquired_characters.forte = true }
|
|
if (data.characters[6]==1) { acquired_characters.doyle = true }
|
|
if (data.characters[7]==1) { acquired_characters.lisa = true }
|
|
|
|
let party_characters = {}
|
|
if (data.party[0]==1) { party_characters.arthur = true }
|
|
if (data.party[1]==1) { party_characters.melody = true }
|
|
if (data.party[2]==1) { party_characters.rodi = true }
|
|
if (data.party[3]==1) { party_characters.basso = true }
|
|
if (data.party[4]==1) { party_characters.akane = true }
|
|
if (data.party[5]==1) { party_characters.forte = true }
|
|
if (data.party[6]==1) { party_characters.doyle = true }
|
|
if (data.party[7]==1) { party_characters.lisa = true }
|
|
|
|
data.party_characters_list = []
|
|
for (let char in party_characters) {
|
|
data.party_characters_list.push(char)
|
|
}
|
|
|
|
data.acquired_characters_list = []
|
|
for (let char in acquired_characters) {
|
|
data.acquired_characters_list.push(char)
|
|
}
|
|
|
|
|
|
for (let char of data.party_characters_list) {
|
|
data.hp[char] = readm(offsets.hp[char])
|
|
data.max_hp[char] = readm(offsets.max_hp[char])
|
|
data.mp[char] = readm(offsets.mp[char])
|
|
data.max_mp[char] = readm(offsets.max_mp[char])
|
|
|
|
|
|
let char_status = readm(offsets.status[char], memoryjs.BYTE)
|
|
|
|
let cstat = 'Healthy'
|
|
if (char_status != 0) {
|
|
if (char_status<=4) {
|
|
cstat = 'Poisoned'
|
|
} else if (char_status>4) {
|
|
cstat = 'Fatally Ill'
|
|
}
|
|
}
|
|
data.status[char] = cstat
|
|
}
|
|
|
|
|
|
let direction = readm(offsets.position.direction, memoryjs.UINT16)
|
|
let steps_in_area = readm(offsets.steps.area)
|
|
let current_location = memoryjs.readMemory(process.handle, core.modBaseAddr+offsets.discrepancy+offsets.eid, memoryjs.UINT16_BE);
|
|
let current_location1 = memoryjs.readMemory(process.handle, core.modBaseAddr+offsets.discrepancy+offsets.event_id, memoryjs.UINT16_BE);
|
|
let worldmap = readm(offsets.map_id, memoryjs.UINT8)
|
|
data.location_id = current_location
|
|
data.location_id1 = current_location1
|
|
data.steps_in_area = steps_in_area
|
|
data.worldmap = worldmap
|
|
|
|
data.position = {
|
|
x: Math.round(readm(offsets.position.x, memoryjs.INT16)/16)/10,
|
|
y: Math.round(readm(offsets.position.y, memoryjs.INT16)/16)/10,
|
|
z: Math.round(readm(offsets.position.z, memoryjs.INT8)),
|
|
z2: Math.round(readm(offsets.position.z2, memoryjs.INT8)),
|
|
north: readm(offsets.position.north, memoryjs.UINT8),
|
|
west: readm(offsets.position.west, memoryjs.UINT8),
|
|
direction : direction/(65536/360),
|
|
}
|
|
|
|
|
|
if (current_location && (last_map_id != current_location) || worldmap != last_worldmap) {
|
|
data.map_changed = true
|
|
maps[last_worldmap] = maps[last_worldmap] || {}
|
|
maps[last_worldmap][last_map_id] = mapped_data
|
|
mapped_data = maps[worldmap]?.[current_location] || {}
|
|
last_map_id = current_location
|
|
last_worldmap = worldmap
|
|
mapped_data.entrance = {x : data.position.x, y : data.position.y }
|
|
mapped_data.map_name = current_location
|
|
mapped_data.event_id = current_location1
|
|
mapped_data.location = data.worldmap
|
|
last_pos.x = data.position.x
|
|
last_pos.y = data.position.y
|
|
reset_cell_size = true
|
|
}
|
|
|
|
|
|
let ingame = (readm(offsets.ingame_indicator, memoryjs.UINT8) == 1)
|
|
|
|
if ( data.worldmap == 244) {
|
|
//data.reset_cell_size = true
|
|
} else {
|
|
if ( ingame &&
|
|
( (last_pos.y != data.position.y) ||
|
|
(last_pos.x != data.position.x) ||
|
|
( (ingame != last_ingame) &&
|
|
last_ingame == 0
|
|
)
|
|
)
|
|
) {
|
|
if (reset_cell_size_sent) {
|
|
reset_cell_size = false
|
|
reset_cell_size_sent = false
|
|
}
|
|
|
|
mapped_data[data.position.x] = mapped_data[data.position.x] || {}
|
|
mapped_data[data.position.x][data.position.y] = mapped_data[data.position.x][data.position.y] || {}
|
|
//if ( Math.abs(last_pos.y - data.position.y) + Math.abs(last_pos.x - data.position.x) > 1) {
|
|
mapped_data[data.position.x][data.position.y] = mapped_data[data.position.x][data.position.y] || {}
|
|
mapped_data[data.position.x][data.position.y].additional = mapped_data[data.position.x][data.position.y].additional || {}
|
|
// this breaks older paths, need multiple Y coords!
|
|
//mapped_data[data.position.x][data.position.y].additional[last_pos.x] = last_pos.y
|
|
mapped_data[data.position.x][data.position.y].additional[last_pos.x] = mapped_data[data.position.x][data.position.y].additional[last_pos.x] || {}
|
|
mapped_data[data.position.x][data.position.y].additional[last_pos.x][last_pos.y] = true
|
|
mapped_data[data.position.x][data.position.y].z = mapped_data[data.position.x][data.position.y].z || {}
|
|
if (data.position.z> 0) {mapped_data[data.position.x][data.position.y].z.up = true}
|
|
if (data.position.z< 0) {mapped_data[data.position.x][data.position.y].z.down = true}
|
|
if (data.position.z==0) {mapped_data[data.position.x][data.position.y].z.middle = true}
|
|
mapped_data[data.position.x][data.position.y].z.z1 = data.position.z
|
|
mapped_data[data.position.x][data.position.y].z.z2 = data.position.z2
|
|
//} else {
|
|
//mapped_data[data.position.x][data.position.y] = true
|
|
//}
|
|
} else {
|
|
|
|
}
|
|
}
|
|
|
|
last_ingame = ingame
|
|
last_pos.x = data.position.x
|
|
last_pos.y = data.position.y
|
|
data.mapped_data = mapped_data
|
|
data.reset_cell_size = reset_cell_size
|
|
////////////
|
|
|
|
let hero_name = []
|
|
let hero_name1 = []
|
|
|
|
let c = 0
|
|
for (let i of offsets.savefile_name) {
|
|
hero_name1[c] = readm(i, memoryjs.CHAR)
|
|
hero_name[c] = tint2char(hero_name1[c])
|
|
c = c+1
|
|
}
|
|
data.hero_name = hero_name
|
|
data.hero_name1 = hero_name1
|
|
|
|
throttle_counter = throttle_counter + 1
|
|
if ((throttle_counter > throttle) || data.map_changed) {
|
|
throttle_counter = 0
|
|
reset_cell_size_sent = true
|
|
reset_cell_size = false
|
|
WSS.send (
|
|
null,
|
|
data
|
|
)
|
|
}
|
|
},
|
|
100
|
|
)
|
|
}
|
|
|
|
new_ws_server(13531)
|
|
|
|
let save_maps = function () {
|
|
fs.writeFile('./maps.json', JSON.stringify(maps || {}) , function() {
|
|
setTimeout(
|
|
save_maps,
|
|
5000
|
|
)
|
|
})
|
|
}
|
|
|
|
save_maps()
|
|
|
|
|
|
let { spawn, exec } = require('child_process');
|
|
let ui = spawn('ui.html', { shell: true, detached : true, stdio: 'ignore' });
|
|
ui.unref();
|
|
|
|
|
|
|
|
//memoryjs.closeProcess(process.handle);
|