| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | local socket = require("socket") | 
					
						
							|  |  |  | local json = require('json') | 
					
						
							|  |  |  | local math = require('math') | 
					
						
							| 
									
										
										
										
											2023-04-15 00:17:33 -07:00
										 |  |  | require("common") | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | local STATE_OK = "Ok" | 
					
						
							|  |  |  | local STATE_TENTATIVELY_CONNECTED = "Tentatively Connected" | 
					
						
							|  |  |  | local STATE_INITIAL_CONNECTION_MADE = "Initial Connection Made" | 
					
						
							|  |  |  | local STATE_UNINITIALIZED = "Uninitialized" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  | local SCRIPT_VERSION = 3 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | local APIndex = 0x1A6E | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  | local APDeathLinkAddress = 0x00FD | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | local APItemAddress = 0x00FF | 
					
						
							|  |  |  | local EventFlagAddress = 0x1735 | 
					
						
							|  |  |  | local MissableAddress = 0x161A | 
					
						
							|  |  |  | local HiddenItemsAddress = 0x16DE | 
					
						
							|  |  |  | local RodAddress = 0x1716 | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  | local DexSanityAddress = 0x1A71 | 
					
						
							|  |  |  | local InGameAddress = 0x1A84 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  | local ClientCompatibilityAddress = 0xFF00 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | local ItemsReceived = nil | 
					
						
							|  |  |  | local playerName = nil | 
					
						
							|  |  |  | local seedName = nil | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  | local deathlink_rec = nil | 
					
						
							|  |  |  | local deathlink_send = false | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | local prevstate = "" | 
					
						
							|  |  |  | local curstate =  STATE_UNINITIALIZED | 
					
						
							|  |  |  | local gbSocket = nil | 
					
						
							|  |  |  | local frame = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  | local compat = nil | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | local function defineMemoryFunctions() | 
					
						
							|  |  |  | 	local memDomain = {} | 
					
						
							|  |  |  | 	local domains = memory.getmemorydomainlist() | 
					
						
							|  |  |  | 	memDomain["rom"] = function() memory.usememorydomain("ROM") end | 
					
						
							|  |  |  | 	memDomain["wram"] = function() memory.usememorydomain("WRAM") end | 
					
						
							|  |  |  | 	return memDomain | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | local memDomain = defineMemoryFunctions() | 
					
						
							|  |  |  | u8 = memory.read_u8 | 
					
						
							|  |  |  | wU8 = memory.write_u8 | 
					
						
							|  |  |  | u16 = memory.read_u16_le | 
					
						
							|  |  |  | function uRange(address, bytes) | 
					
						
							|  |  |  | 	data = memory.readbyterange(address - 1, bytes + 1) | 
					
						
							|  |  |  | 	data[0] = nil | 
					
						
							|  |  |  | 	return data | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function generateLocationsChecked() | 
					
						
							|  |  |  | 	memDomain.wram() | 
					
						
							|  |  |  | 	events = uRange(EventFlagAddress, 0x140) | 
					
						
							|  |  |  | 	missables = uRange(MissableAddress, 0x20) | 
					
						
							|  |  |  | 	hiddenitems = uRange(HiddenItemsAddress, 0x0E) | 
					
						
							| 
									
										
										
										
											2023-05-11 08:52:29 -07:00
										 |  |  | 	rod = {u8(RodAddress)} | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  | 	dexsanity = uRange(DexSanityAddress, 19) | 
					
						
							| 
									
										
										
										
											2023-05-11 08:52:29 -07:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	data = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-11 08:52:29 -07:00
										 |  |  |     categories = {events, missables, hiddenitems, rod} | 
					
						
							|  |  |  |     if compat > 1 then | 
					
						
							|  |  |  |         table.insert(categories, dexsanity) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     for _, category in ipairs(categories) do | 
					
						
							|  |  |  |         for _, v in ipairs(category) do  | 
					
						
							|  |  |  |             table.insert(data, v)  | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return data | 
					
						
							|  |  |  | end | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | local function arrayEqual(a1, a2) | 
					
						
							| 
									
										
										
										
											2023-04-15 00:17:33 -07:00
										 |  |  |     if #a1 ~= #a2 then | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |       return false | 
					
						
							|  |  |  |     end | 
					
						
							| 
									
										
										
										
											2023-04-15 00:17:33 -07:00
										 |  |  |    | 
					
						
							|  |  |  |     for i, v in ipairs(a1) do | 
					
						
							|  |  |  |       if v ~= a2[i] then | 
					
						
							|  |  |  |         return false | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |     return true | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function receive() | 
					
						
							|  |  |  |     l, e = gbSocket:receive() | 
					
						
							|  |  |  |     if e == 'closed' then | 
					
						
							|  |  |  |         if curstate == STATE_OK then | 
					
						
							|  |  |  |             print("Connection closed") | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         curstate = STATE_UNINITIALIZED | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     elseif e == 'timeout' then | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     elseif e ~= nil then | 
					
						
							|  |  |  |         print(e) | 
					
						
							|  |  |  |         curstate = STATE_UNINITIALIZED | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     if l ~= nil then | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  |         block = json.decode(l) | 
					
						
							|  |  |  |         if block ~= nil then | 
					
						
							|  |  |  |             local itemsBlock = block["items"] | 
					
						
							|  |  |  |             if itemsBlock ~= nil then | 
					
						
							|  |  |  |                 ItemsReceived = itemsBlock | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |             deathlink_rec = block["deathlink"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     end | 
					
						
							|  |  |  |     -- Determine Message to send back | 
					
						
							|  |  |  |     memDomain.rom() | 
					
						
							|  |  |  |     newPlayerName = uRange(0xFFF0, 0x10) | 
					
						
							| 
									
										
										
										
											2022-11-01 02:02:15 -04:00
										 |  |  |     newSeedName = uRange(0xFFDB, 21) | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     if (playerName ~= nil and not arrayEqual(playerName, newPlayerName)) or (seedName ~= nil and not arrayEqual(seedName, newSeedName)) then | 
					
						
							|  |  |  |         print("ROM changed, quitting") | 
					
						
							|  |  |  |         curstate = STATE_UNINITIALIZED | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     playerName = newPlayerName | 
					
						
							|  |  |  |     seedName = newSeedName | 
					
						
							|  |  |  |     local retTable = {} | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     retTable["scriptVersion"] = SCRIPT_VERSION | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if compat == nil then | 
					
						
							|  |  |  |         compat = u8(ClientCompatibilityAddress) | 
					
						
							|  |  |  |         if compat < 2 then | 
					
						
							|  |  |  |             InGameAddress = 0x1A71 | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     retTable["clientCompatibilityVersion"] = compat | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     retTable["playerName"] = playerName | 
					
						
							|  |  |  |     retTable["seedName"] = seedName | 
					
						
							|  |  |  |     memDomain.wram() | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     in_game = u8(InGameAddress) | 
					
						
							|  |  |  |     if in_game == 0x2A or in_game == 0xAC then | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         retTable["locations"] = generateLocationsChecked() | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  |     elseif in_game ~= 0 then | 
					
						
							|  |  |  |         print("Game may have crashed") | 
					
						
							|  |  |  |         curstate = STATE_UNINITIALIZED | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     end | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-07 18:38:34 -05:00
										 |  |  |     retTable["deathLink"] = deathlink_send | 
					
						
							|  |  |  |     deathlink_send = false | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |     msg = json.encode(retTable).."\n" | 
					
						
							|  |  |  |     local ret, error = gbSocket:send(msg) | 
					
						
							|  |  |  |     if ret == nil then | 
					
						
							|  |  |  |         print(error) | 
					
						
							|  |  |  |     elseif curstate == STATE_INITIAL_CONNECTION_MADE then | 
					
						
							|  |  |  |         curstate = STATE_TENTATIVELY_CONNECTED | 
					
						
							|  |  |  |     elseif curstate == STATE_TENTATIVELY_CONNECTED then | 
					
						
							|  |  |  |         print("Connected!") | 
					
						
							|  |  |  |         curstate = STATE_OK | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function main() | 
					
						
							| 
									
										
										
										
											2023-06-26 16:53:44 +10:00
										 |  |  |     if not checkBizHawkVersion() then | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         return | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |     server, error = socket.bind('localhost', 17242) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while true do | 
					
						
							| 
									
										
										
										
											2022-11-09 09:15:16 -05:00
										 |  |  |         frame = frame + 1 | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |         if not (curstate == prevstate) then | 
					
						
							|  |  |  |             print("Current state: "..curstate) | 
					
						
							|  |  |  |             prevstate = curstate | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then | 
					
						
							| 
									
										
										
										
											2022-11-09 09:15:16 -05:00
										 |  |  |             if (frame % 5 == 0) then | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                 receive() | 
					
						
							| 
									
										
										
										
											2023-03-13 18:40:55 -04:00
										 |  |  |                 in_game = u8(InGameAddress) | 
					
						
							|  |  |  |                 if in_game == 0x2A or in_game == 0xAC then | 
					
						
							|  |  |  |                     if u8(APItemAddress) == 0x00 then | 
					
						
							|  |  |  |                         ItemIndex = u16(APIndex) | 
					
						
							|  |  |  |                         if deathlink_rec == true then | 
					
						
							|  |  |  |                             wU8(APDeathLinkAddress, 1) | 
					
						
							|  |  |  |                         elseif u8(APDeathLinkAddress) == 3 then | 
					
						
							|  |  |  |                             wU8(APDeathLinkAddress, 0) | 
					
						
							|  |  |  |                             deathlink_send = true | 
					
						
							|  |  |  |                         end | 
					
						
							|  |  |  |                         if ItemsReceived[ItemIndex + 1] ~= nil then | 
					
						
							|  |  |  |                             item_id = ItemsReceived[ItemIndex + 1] - 172000000 | 
					
						
							|  |  |  |                             if item_id > 255 then | 
					
						
							|  |  |  |                                 item_id = item_id - 256 | 
					
						
							|  |  |  |                             end | 
					
						
							|  |  |  |                             wU8(APItemAddress, item_id) | 
					
						
							|  |  |  |                         end | 
					
						
							| 
									
										
										
										
											2022-10-13 01:45:52 -04:00
										 |  |  |                     end | 
					
						
							|  |  |  |                 end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |         elseif (curstate == STATE_UNINITIALIZED) then | 
					
						
							|  |  |  |             if  (frame % 60 == 0) then | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 print("Waiting for client.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 emu.frameadvance() | 
					
						
							|  |  |  |                 server:settimeout(2) | 
					
						
							|  |  |  |                 print("Attempting to connect") | 
					
						
							|  |  |  |                 local client, timeout = server:accept() | 
					
						
							|  |  |  |                 if timeout == nil then | 
					
						
							|  |  |  |                     curstate = STATE_INITIAL_CONNECTION_MADE | 
					
						
							|  |  |  |                     gbSocket = client | 
					
						
							|  |  |  |                     gbSocket:settimeout(0) | 
					
						
							|  |  |  |                 end | 
					
						
							|  |  |  |             end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |         emu.frameadvance() | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | main() |