TLoZ: Implementing The Legend of Zelda (#1354)
Co-authored-by: t3hf1gm3nt <59876300+t3hf1gm3nt@users.noreply.github.com> Co-authored-by: Alchav <59858495+Alchav@users.noreply.github.com>
This commit is contained in:
		
							
								
								
									
										702
									
								
								data/lua/TLoZ/TheLegendOfZeldaConnector.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										702
									
								
								data/lua/TLoZ/TheLegendOfZeldaConnector.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,702 @@ | ||||
| --Shamelessly based off the FF1 lua | ||||
|  | ||||
| local socket = require("socket") | ||||
| local json = require('json') | ||||
| local math = require('math') | ||||
|  | ||||
| local STATE_OK = "Ok" | ||||
| local STATE_TENTATIVELY_CONNECTED = "Tentatively Connected" | ||||
| local STATE_INITIAL_CONNECTION_MADE = "Initial Connection Made" | ||||
| local STATE_UNINITIALIZED = "Uninitialized" | ||||
|  | ||||
| local itemMessages = {} | ||||
| local consumableStacks = nil | ||||
| local prevstate = "" | ||||
| local curstate =  STATE_UNINITIALIZED | ||||
| local zeldaSocket = nil | ||||
| local frame = 0 | ||||
| local gameMode = 0 | ||||
|  | ||||
| local cave_index | ||||
| local triforce_byte | ||||
| local game_state | ||||
|  | ||||
| local u8 = nil | ||||
| local wU8 = nil | ||||
| local isNesHawk = false | ||||
|  | ||||
| local shopsChecked = {} | ||||
| local shopSlotLeft       = 0x0628 | ||||
| local shopSlotMiddle     = 0x0629 | ||||
| local shopSlotRight      = 0x062A | ||||
|  | ||||
| --N.B.: you won't find these in a RAM map. They're flag values that the base patch derives from the cave ID. | ||||
| local blueRingShopBit       = 0x40 | ||||
| local potionShopBit         = 0x02 | ||||
| local arrowShopBit          = 0x08 | ||||
| local candleShopBit         = 0x10 | ||||
| local shieldShopBit         = 0x20 | ||||
| local takeAnyCaveBit        = 0x01 | ||||
|  | ||||
|  | ||||
| local sword                 = 0x0657 | ||||
| local bombs                 = 0x0658 | ||||
| local maxBombs              = 0x067C | ||||
| local keys                  = 0x066E | ||||
| local arrow                 = 0x0659 | ||||
| local bow                   = 0x065A | ||||
| local candle                = 0x065B | ||||
| local recorder              = 0x065C | ||||
| local food                  = 0x065D | ||||
| local waterOfLife           = 0x065E | ||||
| local magicalRod            = 0x065F | ||||
| local raft                  = 0x0660 | ||||
| local bookOfMagic           = 0x0661 | ||||
| local ring                  = 0x0662 | ||||
| local stepladder            = 0x0663 | ||||
| local magicalKey            = 0x0664 | ||||
| local powerBracelet         = 0x0665 | ||||
| local letter                = 0x0666 | ||||
| local clockItem             = 0x066C | ||||
| local heartContainers       = 0x066F | ||||
| local partialHearts         = 0x0670 | ||||
| local triforceFragments     = 0x0671 | ||||
| local boomerang             = 0x0674 | ||||
| local magicalBoomerang      = 0x0675 | ||||
| local magicalShield         = 0x0676 | ||||
| local rupeesToAdd           = 0x067D | ||||
| local rupeesToSubtract      = 0x067E | ||||
| local itemsObtained         = 0x0677 | ||||
| local takeAnyCavesChecked   = 0x0678 | ||||
| local localTriforce         = 0x0679 | ||||
| local bonusItemsObtained    = 0x067A | ||||
|  | ||||
| itemAPids = { | ||||
|     ["Boomerang"] = 7100, | ||||
|     ["Bow"] = 7101, | ||||
|     ["Magical Boomerang"] = 7102, | ||||
|     ["Raft"] = 7103, | ||||
|     ["Stepladder"] = 7104, | ||||
|     ["Recorder"] = 7105, | ||||
|     ["Magical Rod"] = 7106, | ||||
|     ["Red Candle"] = 7107, | ||||
|     ["Book of Magic"] = 7108, | ||||
|     ["Magical Key"] = 7109, | ||||
|     ["Red Ring"] = 7110, | ||||
|     ["Silver Arrow"] = 7111, | ||||
|     ["Sword"] = 7112, | ||||
|     ["White Sword"] = 7113, | ||||
|     ["Magical Sword"] = 7114, | ||||
|     ["Heart Container"] = 7115, | ||||
|     ["Letter"] = 7116, | ||||
|     ["Magical Shield"] = 7117, | ||||
|     ["Candle"] = 7118, | ||||
|     ["Arrow"] = 7119, | ||||
|     ["Food"] = 7120, | ||||
|     ["Water of Life (Blue)"] = 7121, | ||||
|     ["Water of Life (Red)"] = 7122, | ||||
|     ["Blue Ring"] = 7123, | ||||
|     ["Triforce Fragment"] = 7124, | ||||
|     ["Power Bracelet"] = 7125, | ||||
|     ["Small Key"] = 7126, | ||||
|     ["Bomb"] = 7127, | ||||
|     ["Recovery Heart"] = 7128, | ||||
|     ["Five Rupees"] = 7129, | ||||
|     ["Rupee"] = 7130, | ||||
|     ["Clock"] = 7131, | ||||
|     ["Fairy"] = 7132 | ||||
| } | ||||
|  | ||||
| itemCodes = { | ||||
|     ["Boomerang"] = 0x1D, | ||||
|     ["Bow"] = 0x0A, | ||||
|     ["Magical Boomerang"] = 0x1E, | ||||
|     ["Raft"] = 0x0C, | ||||
|     ["Stepladder"] = 0x0D, | ||||
|     ["Recorder"] = 0x05, | ||||
|     ["Magical Rod"] = 0x10, | ||||
|     ["Red Candle"] = 0x07, | ||||
|     ["Book of Magic"] = 0x11, | ||||
|     ["Magical Key"] = 0x0B, | ||||
|     ["Red Ring"] = 0x13, | ||||
|     ["Silver Arrow"] = 0x09, | ||||
|     ["Sword"] = 0x01, | ||||
|     ["White Sword"] = 0x02, | ||||
|     ["Magical Sword"] = 0x03, | ||||
|     ["Heart Container"] = 0x1A, | ||||
|     ["Letter"] = 0x15, | ||||
|     ["Magical Shield"] = 0x1C, | ||||
|     ["Candle"] = 0x06, | ||||
|     ["Arrow"] = 0x08, | ||||
|     ["Food"] = 0x04, | ||||
|     ["Water of Life (Blue)"] = 0x1F, | ||||
|     ["Water of Life (Red)"] = 0x20, | ||||
|     ["Blue Ring"] = 0x12, | ||||
|     ["Triforce Fragment"] = 0x1B, | ||||
|     ["Power Bracelet"] = 0x14, | ||||
|     ["Small Key"] = 0x19, | ||||
|     ["Bomb"] = 0x00, | ||||
|     ["Recovery Heart"] = 0x22, | ||||
|     ["Five Rupees"] = 0x0F, | ||||
|     ["Rupee"] = 0x18, | ||||
|     ["Clock"] = 0x21, | ||||
|     ["Fairy"] = 0x23 | ||||
| } | ||||
|  | ||||
|  | ||||
| --Sets correct memory access functions based on whether NesHawk or QuickNES is loaded | ||||
| local function defineMemoryFunctions() | ||||
| 	local memDomain = {} | ||||
| 	local domains = memory.getmemorydomainlist() | ||||
| 	if domains[1] == "System Bus" then | ||||
| 		--NesHawk | ||||
| 		isNesHawk = true | ||||
| 		memDomain["systembus"] = function() memory.usememorydomain("System Bus") end | ||||
|         memDomain["ram"]       = function() memory.usememorydomain("RAM") end | ||||
| 		memDomain["saveram"]   = function() memory.usememorydomain("Battery RAM") end | ||||
| 		memDomain["rom"]       = function() memory.usememorydomain("PRG ROM") end | ||||
| 	elseif domains[1] == "WRAM" then | ||||
| 		--QuickNES | ||||
| 		memDomain["systembus"] = function() memory.usememorydomain("System Bus") end | ||||
|         memDomain["ram"]       = function() memory.usememorydomain("RAM") end | ||||
| 		memDomain["saveram"]   = function() memory.usememorydomain("WRAM") end | ||||
| 		memDomain["rom"]       = function() memory.usememorydomain("PRG ROM") end | ||||
| 	end | ||||
| 	return memDomain | ||||
| end | ||||
|  | ||||
| local memDomain = defineMemoryFunctions() | ||||
| u8 = memory.read_u8 | ||||
| wU8 = memory.write_u8 | ||||
| uRange = memory.readbyterange | ||||
|  | ||||
| itemIDNames = {} | ||||
|  | ||||
| for key, value in pairs(itemAPids) do | ||||
|     itemIDNames[value] = key | ||||
| end | ||||
|  | ||||
|  | ||||
|  | ||||
| local function determineItem(array) | ||||
|     memdomain.ram() | ||||
|     currentItemsObtained = u8(itemsObtained) | ||||
|      | ||||
| end | ||||
|  | ||||
| local function gotSword() | ||||
|     local currentSword = u8(sword) | ||||
|     wU8(sword, math.max(currentSword, 1)) | ||||
| end | ||||
|  | ||||
| local function gotWhiteSword() | ||||
|     local currentSword = u8(sword) | ||||
|     wU8(sword, math.max(currentSword, 2)) | ||||
| end | ||||
|  | ||||
| local function gotMagicalSword() | ||||
|     wU8(sword, 3) | ||||
| end | ||||
|  | ||||
| local function gotBomb() | ||||
|     local currentBombs = u8(bombs) | ||||
|     local currentMaxBombs = u8(maxBombs) | ||||
|     wU8(bombs, math.min(currentBombs + 4, currentMaxBombs)) | ||||
|     wU8(0x505, 0x29) -- Fake bomb to show item get. | ||||
| end | ||||
|  | ||||
| local function gotArrow() | ||||
|     local currentArrow = u8(arrow) | ||||
|     wU8(arrow, math.max(currentArrow, 1)) | ||||
| end | ||||
|  | ||||
| local function gotSilverArrow() | ||||
|     wU8(arrow, 2) | ||||
| end | ||||
|  | ||||
| local function gotBow() | ||||
|     wU8(bow, 1) | ||||
| end | ||||
|  | ||||
| local function gotCandle() | ||||
|     local currentCandle = u8(candle) | ||||
|     wU8(candle, math.max(currentCandle, 1)) | ||||
| end | ||||
|  | ||||
| local function gotRedCandle() | ||||
|     wU8(candle, 2) | ||||
| end | ||||
|  | ||||
| local function gotRecorder() | ||||
|     wU8(recorder, 1) | ||||
| end | ||||
|  | ||||
| local function gotFood() | ||||
|     wU8(food, 1) | ||||
| end | ||||
|  | ||||
| local function gotWaterOfLifeBlue() | ||||
|     local currentWaterOfLife = u8(waterOfLife) | ||||
|     wU8(waterOfLife, math.max(currentWaterOfLife, 1)) | ||||
| end | ||||
|  | ||||
| local function gotWaterOfLifeRed() | ||||
|     wU8(waterOfLife, 2) | ||||
| end | ||||
|  | ||||
| local function gotMagicalRod() | ||||
|     wU8(magicalRod, 1) | ||||
| end | ||||
|  | ||||
| local function gotBookOfMagic() | ||||
|     wU8(bookOfMagic, 1) | ||||
| end | ||||
|  | ||||
| local function gotRaft() | ||||
|     wU8(raft, 1) | ||||
| end | ||||
|  | ||||
| local function gotBlueRing() | ||||
|     local currentRing = u8(ring) | ||||
|     wU8(ring, math.max(currentRing, 1)) | ||||
|     memDomain.saveram() | ||||
|     local currentTunicColor = u8(0x0B92) | ||||
|     if currentTunicColor == 0x29 then  | ||||
|         wU8(0x0B92, 0x32) | ||||
|         wU8(0x0804, 0x32) | ||||
|     end | ||||
| end | ||||
|  | ||||
| local function gotRedRing() | ||||
|     wU8(ring, 2) | ||||
|     memDomain.saveram() | ||||
|     wU8(0x0B92, 0x16) | ||||
|     wU8(0x0804, 0x16) | ||||
| end | ||||
|  | ||||
| local function gotStepladder() | ||||
|     wU8(stepladder, 1) | ||||
| end | ||||
|  | ||||
| local function gotMagicalKey() | ||||
|     wU8(magicalKey, 1) | ||||
| end | ||||
|  | ||||
| local function gotPowerBracelet() | ||||
|     wU8(powerBracelet, 1) | ||||
| end | ||||
|  | ||||
| local function gotLetter() | ||||
|     wU8(letter, 1) | ||||
| end | ||||
|  | ||||
| local function gotHeartContainer() | ||||
|     local currentHeartContainers = bit.rshift(bit.band(u8(heartContainers), 0xF0), 4) | ||||
|     if currentHeartContainers < 16 then | ||||
|         currentHeartContainers = math.min(currentHeartContainers + 1, 16) | ||||
|         local currentHearts = bit.band(u8(heartContainers), 0x0F) + 1 | ||||
|         wU8(heartContainers, bit.lshift(currentHeartContainers, 4) + currentHearts) | ||||
|     end | ||||
| end | ||||
|  | ||||
| local function gotTriforceFragment() | ||||
|     local triforceByte = 0xFF | ||||
|     local newTriforceCount = u8(localTriforce) + 1 | ||||
|     wU8(localTriforce, newTriforceCount) | ||||
| end | ||||
|  | ||||
| local function gotBoomerang() | ||||
|     wU8(boomerang, 1) | ||||
| end | ||||
|  | ||||
| local function gotMagicalBoomerang() | ||||
|     wU8(magicalBoomerang, 1) | ||||
| end | ||||
|  | ||||
| local function gotMagicalShield() | ||||
|     wU8(magicalShield, 1) | ||||
| end | ||||
|  | ||||
| local function gotRecoveryHeart() | ||||
|     local currentHearts = bit.band(u8(heartContainers), 0x0F) | ||||
|     local currentHeartContainers = bit.rshift(bit.band(u8(heartContainers), 0xF0), 4) | ||||
|     if currentHearts < currentHeartContainers then  | ||||
|         currentHearts = currentHearts + 1  | ||||
|     else | ||||
|         wU8(partialHearts, 0xFF) | ||||
|     end | ||||
|     currentHearts = bit.bor(bit.band(u8(heartContainers), 0xF0), currentHearts) | ||||
|     wU8(heartContainers, currentHearts) | ||||
| end | ||||
|  | ||||
| local function gotFairy() | ||||
|     local currentHearts = bit.band(u8(heartContainers), 0x0F) | ||||
|     local currentHeartContainers = bit.rshift(bit.band(u8(heartContainers), 0xF0), 4) | ||||
|     if currentHearts < currentHeartContainers then  | ||||
|         currentHearts = currentHearts + 3 | ||||
|         if currentHearts > currentHeartContainers then | ||||
|             currentHearts = currentHeartContainers | ||||
|             wU8(partialHearts, 0xFF) | ||||
|         end | ||||
|     else | ||||
|         wU8(partialHearts, 0xFF) | ||||
|     end | ||||
|     currentHearts = bit.bor(bit.band(u8(heartContainers), 0xF0), currentHearts) | ||||
|     wU8(heartContainers, currentHearts) | ||||
| end | ||||
|  | ||||
| local function gotClock() | ||||
|     wU8(clockItem, 1) | ||||
| end | ||||
|  | ||||
| local function gotFiveRupees() | ||||
|     local currentRupeesToAdd = u8(rupeesToAdd) | ||||
|     wU8(rupeesToAdd, math.min(currentRupeesToAdd + 5, 255)) | ||||
| end | ||||
|  | ||||
| local function gotSmallKey() | ||||
|     wU8(keys, math.min(u8(keys) + 1, 9)) | ||||
| end | ||||
|  | ||||
| local function gotItem(item) | ||||
|     --Write itemCode to itemToLift | ||||
|     --Write 128 to itemLiftTimer | ||||
|     --Write 4 to sound effect queue | ||||
|     itemName = itemIDNames[item] | ||||
|     itemCode = itemCodes[itemName] | ||||
|     wU8(0x505, itemCode) | ||||
|     wU8(0x506, 128) | ||||
|     wU8(0x602, 4) | ||||
|     numberObtained = u8(itemsObtained) + 1 | ||||
|     wU8(itemsObtained, numberObtained) | ||||
|     if itemName == "Boomerang" then gotBoomerang() end | ||||
|     if itemName == "Bow" then gotBow() end | ||||
|     if itemName == "Magical Boomerang" then gotMagicalBoomerang() end | ||||
|     if itemName == "Raft" then gotRaft() end | ||||
|     if itemName == "Stepladder" then gotStepladder() end | ||||
|     if itemName == "Recorder" then gotRecorder() end | ||||
|     if itemName == "Magical Rod" then gotMagicalRod() end | ||||
|     if itemName == "Red Candle" then gotRedCandle() end | ||||
|     if itemName == "Book of Magic" then gotBookOfMagic() end | ||||
|     if itemName == "Magical Key" then gotMagicalKey() end | ||||
|     if itemName == "Red Ring" then gotRedRing() end | ||||
|     if itemName == "Silver Arrow" then gotSilverArrow() end | ||||
|     if itemName == "Sword" then gotSword() end | ||||
|     if itemName == "White Sword" then gotWhiteSword() end | ||||
|     if itemName == "Magical Sword" then gotMagicalSword() end | ||||
|     if itemName == "Heart Container" then gotHeartContainer() end | ||||
|     if itemName == "Letter" then gotLetter() end | ||||
|     if itemName == "Magical Shield" then gotMagicalShield() end | ||||
|     if itemName == "Candle" then gotCandle() end | ||||
|     if itemName == "Arrow" then gotArrow() end | ||||
|     if itemName == "Food" then gotFood() end | ||||
|     if itemName == "Water of Life (Blue)" then gotWaterOfLifeBlue() end | ||||
|     if itemName == "Water of Life (Red)" then gotWaterOfLifeRed() end | ||||
|     if itemName == "Blue Ring" then gotBlueRing() end | ||||
|     if itemName == "Triforce Fragment" then gotTriforceFragment() end | ||||
|     if itemName == "Power Bracelet" then gotPowerBracelet() end | ||||
|     if itemName == "Small Key" then gotSmallKey() end | ||||
|     if itemName == "Bomb" then gotBomb() end | ||||
|     if itemName == "Recovery Heart" then gotRecoveryHeart() end | ||||
|     if itemName == "Five Rupees" then gotFiveRupees() end | ||||
|     if itemName == "Fairy" then gotFairy() end | ||||
|     if itemName == "Clock" then gotClock() end | ||||
| end | ||||
|  | ||||
|  | ||||
| local function StateOKForMainLoop() | ||||
|     memDomain.ram() | ||||
|     local gameMode = u8(0x12)  | ||||
|     return gameMode == 5  | ||||
| end | ||||
|  | ||||
| local function checkCaveItemObtained() | ||||
|     memDomain.ram()  | ||||
|     local returnTable = {} | ||||
|     returnTable["slot1"] = u8(shopSlotLeft) | ||||
|     returnTable["slot2"] = u8(shopSlotMiddle) | ||||
|     returnTable["slot3"] = u8(shopSlotRight) | ||||
|     returnTable["takeAnys"] = u8(takeAnyCavesChecked) | ||||
|     return returnTable | ||||
| end | ||||
|  | ||||
| function table.empty (self) | ||||
|     for _, _ in pairs(self) do | ||||
|         return false | ||||
|     end | ||||
|     return true | ||||
| end | ||||
|  | ||||
| function slice (tbl, s, e) | ||||
|     local pos, new = 1, {} | ||||
|     for i = s + 1, e do | ||||
|         new[pos] = tbl[i] | ||||
|         pos = pos + 1 | ||||
|     end | ||||
|     return new | ||||
| end | ||||
|  | ||||
| local bizhawk_version = client.getversion() | ||||
| local is23Or24Or25 = (bizhawk_version=="2.3.1") or (bizhawk_version:sub(1,3)=="2.4") or (bizhawk_version:sub(1,3)=="2.5") | ||||
| local is26To28 =  (bizhawk_version:sub(1,3)=="2.6") or (bizhawk_version:sub(1,3)=="2.7") or (bizhawk_version:sub(1,3)=="2.8") | ||||
|  | ||||
| local function getMaxMessageLength() | ||||
|     if is23Or24Or25 then | ||||
|         return client.screenwidth()/11 | ||||
|     elseif is26To28 then | ||||
|         return client.screenwidth()/12 | ||||
|     end | ||||
| end | ||||
|  | ||||
| local function drawText(x, y, message, color) | ||||
|     if is23Or24Or25 then | ||||
|         gui.addmessage(message) | ||||
|     elseif is26To28 then | ||||
|         gui.drawText(x, y, message, color, 0xB0000000, 18, "Courier New", "middle", "bottom", nil, "client") | ||||
|     end | ||||
| end | ||||
|  | ||||
| local function clearScreen() | ||||
|     if is23Or24Or25 then | ||||
|         return | ||||
|     elseif is26To28 then | ||||
|         drawText(0, 0, "", "black") | ||||
|     end | ||||
| end | ||||
|  | ||||
| local function drawMessages() | ||||
|     if table.empty(itemMessages) then | ||||
|         clearScreen() | ||||
|         return | ||||
|     end | ||||
|     local y = 10 | ||||
|     found = false | ||||
|     maxMessageLength = getMaxMessageLength() | ||||
|     for k, v in pairs(itemMessages) do | ||||
|         if v["TTL"] > 0 then | ||||
|             message = v["message"] | ||||
|             while true do | ||||
|                 drawText(5, y, message:sub(1, maxMessageLength), v["color"]) | ||||
|                 y = y + 16 | ||||
|  | ||||
|                 message = message:sub(maxMessageLength + 1, message:len()) | ||||
|                 if message:len() == 0 then | ||||
|                     break | ||||
|                 end | ||||
|             end | ||||
|             newTTL = 0 | ||||
|             if is26To28 then | ||||
|                 newTTL = itemMessages[k]["TTL"] - 1 | ||||
|             end | ||||
|             itemMessages[k]["TTL"] = newTTL | ||||
|             found = true | ||||
|         end | ||||
|     end | ||||
|     if found == false then | ||||
|         clearScreen() | ||||
|     end | ||||
| end | ||||
|  | ||||
| function generateOverworldLocationChecked() | ||||
|     memDomain.ram() | ||||
|     data = uRange(0x067E, 0x81) | ||||
|     data[0] = nil | ||||
|     return data | ||||
| end | ||||
|  | ||||
| function getHCLocation() | ||||
|     memDomain.rom() | ||||
|     data = u8(0x1789A) | ||||
|     return data | ||||
| end | ||||
|  | ||||
| function getPBLocation() | ||||
|     memDomain.rom() | ||||
|     data = u8(0x10CB2) | ||||
|     return data | ||||
| end | ||||
|  | ||||
| function generateUnderworld16LocationChecked() | ||||
|     memDomain.ram() | ||||
|     data = uRange(0x06FE, 0x81) | ||||
|     data[0] = nil | ||||
|     return data | ||||
| end | ||||
|  | ||||
| function generateUnderworld79LocationChecked() | ||||
|     memDomain.ram() | ||||
|     data = uRange(0x077E, 0x81) | ||||
|     data[0] = nil | ||||
|     return data | ||||
| end | ||||
|  | ||||
| function updateTriforceFragments() | ||||
|     memDomain.ram() | ||||
|     local triforceByte = 0xFF | ||||
|     totalTriforceCount = u8(localTriforce) | ||||
|     local currentPieces = bit.rshift(triforceByte, 8 - math.min(8, totalTriforceCount)) | ||||
|     wU8(triforceFragments, currentPieces) | ||||
| end | ||||
|  | ||||
| function processBlock(block) | ||||
|     if block ~= nil then | ||||
|         local msgBlock = block['messages'] | ||||
|         if msgBlock ~= nil then | ||||
|             for i, v in pairs(msgBlock) do | ||||
|                 if itemMessages[i] == nil then | ||||
|                     local msg = {TTL=450, message=v, color=0xFFFF0000} | ||||
|                     itemMessages[i] = msg | ||||
|                 end | ||||
|             end | ||||
|         end | ||||
|         local bonusItems = block["bonusItems"] | ||||
|         if bonusItems ~= nil and isInGame then | ||||
|             for i, item in ipairs(bonusItems) do | ||||
|                 memDomain.ram() | ||||
|                 if i > u8(bonusItemsObtained) then | ||||
|                     if u8(0x505) == 0 then | ||||
|                         gotItem(item) | ||||
|                         wU8(itemsObtained, u8(itemsObtained) - 1) | ||||
|                         wU8(bonusItemsObtained, u8(bonusItemsObtained) + 1) | ||||
|                     end | ||||
|                 end | ||||
|             end | ||||
|         end | ||||
|         local itemsBlock = block["items"] | ||||
|         memDomain.saveram() | ||||
|         isInGame = StateOKForMainLoop() | ||||
|         updateTriforceFragments() | ||||
|         if itemsBlock ~= nil and isInGame then | ||||
|             memDomain.ram() | ||||
|             --get item from item code | ||||
|             --get function from item | ||||
|             --do function | ||||
|             for i, item in ipairs(itemsBlock) do | ||||
|                 memDomain.ram() | ||||
|                 if u8(0x505) == 0 then | ||||
|                     if i > u8(itemsObtained) then | ||||
|                         gotItem(item) | ||||
|                     end | ||||
|                 end | ||||
|             end | ||||
|         end | ||||
|         local shopsBlock = block["shops"] | ||||
|         if shopsBlock ~= nil then | ||||
|             wU8(shopSlotLeft, bit.bor(u8(shopSlotLeft), shopsBlock["left"])) | ||||
|             wU8(shopSlotMiddle, bit.bor(u8(shopSlotMiddle), shopsBlock["middle"])) | ||||
|             wU8(shopSlotRight, bit.bor(u8(shopSlotRight), shopsBlock["right"])) | ||||
|         end | ||||
|     end | ||||
| end | ||||
|  | ||||
| function difference(a, b) | ||||
|     local aa = {} | ||||
|     for k,v in pairs(a) do aa[v]=true end | ||||
|     for k,v in pairs(b) do aa[v]=nil end | ||||
|     local ret = {} | ||||
|     local n = 0 | ||||
|     for k,v in pairs(a) do | ||||
|         if aa[v] then n=n+1 ret[n]=v end | ||||
|     end | ||||
|     return ret | ||||
| end | ||||
|  | ||||
| function receive() | ||||
|     l, e = zeldaSocket:receive() | ||||
|     if e == 'closed' then | ||||
|         if curstate == STATE_OK then | ||||
|             print("Connection closed") | ||||
|         end | ||||
|         curstate = STATE_UNINITIALIZED | ||||
|         return | ||||
|     elseif e == 'timeout' then | ||||
|         print("timeout") | ||||
|         return | ||||
|     elseif e ~= nil then | ||||
|         print(e) | ||||
|         curstate = STATE_UNINITIALIZED | ||||
|         return | ||||
|     end | ||||
|     processBlock(json.decode(l)) | ||||
|  | ||||
|     -- Determine Message to send back | ||||
|     memDomain.rom() | ||||
|     local playerName = uRange(0x1F, 0x10) | ||||
|     playerName[0] = nil | ||||
|     local retTable = {} | ||||
|     retTable["playerName"] = playerName | ||||
|     if StateOKForMainLoop() then | ||||
|         retTable["overworld"] = generateOverworldLocationChecked() | ||||
|         retTable["underworld1"] = generateUnderworld16LocationChecked() | ||||
|         retTable["underworld2"] = generateUnderworld79LocationChecked() | ||||
|     end | ||||
|     retTable["caves"] = checkCaveItemObtained() | ||||
|     memDomain.ram() | ||||
|     if gameMode ~= 19 then | ||||
|         gameMode = u8(0x12) | ||||
|     end | ||||
|     retTable["gameMode"] = gameMode | ||||
|     retTable["overworldHC"] = getHCLocation() | ||||
|     retTable["overworldPB"] = getPBLocation() | ||||
|     retTable["itemsObtained"] = u8(itemsObtained) | ||||
|     msg = json.encode(retTable).."\n" | ||||
|     local ret, error = zeldaSocket: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!") | ||||
|         itemMessages["(0,0)"] = {TTL=240, message="Connected", color="green"} | ||||
|         curstate = STATE_OK | ||||
|     end | ||||
| end | ||||
|  | ||||
| function main() | ||||
|     if (is23Or24Or25 or is26To28) == false then | ||||
|         print("Must use a version of bizhawk 2.3.1 or higher") | ||||
|         return | ||||
|     end | ||||
|     server, error = socket.bind('localhost', 52980) | ||||
|  | ||||
|     while true do | ||||
|         gui.drawEllipse(248, 9, 6, 6, "Black", "Yellow") | ||||
|         frame = frame + 1 | ||||
|         drawMessages() | ||||
|         if not (curstate == prevstate) then | ||||
|             -- console.log("Current state: "..curstate) | ||||
|             prevstate = curstate | ||||
|         end | ||||
|         if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then | ||||
|             if (frame % 60 == 0) then | ||||
|                 gui.drawEllipse(248, 9, 6, 6, "Black", "Blue") | ||||
|                 receive() | ||||
|             else | ||||
|                 gui.drawEllipse(248, 9, 6, 6, "Black", "Green") | ||||
|             end | ||||
|         elseif (curstate == STATE_UNINITIALIZED) then | ||||
|             gui.drawEllipse(248, 9, 6, 6, "Black", "White") | ||||
|             if  (frame % 60 == 0) then | ||||
|                 gui.drawEllipse(248, 9, 6, 6, "Black", "Yellow") | ||||
|  | ||||
|                 drawText(5, 8, "Waiting for client", 0xFFFF0000) | ||||
|                 drawText(5, 32, "Please start Zelda1Client.exe", 0xFFFF0000) | ||||
|  | ||||
|                 -- Advance so the messages are drawn | ||||
|                 emu.frameadvance() | ||||
|                 server:settimeout(2) | ||||
|                 print("Attempting to connect") | ||||
|                 local client, timeout = server:accept() | ||||
|                 if timeout == nil then | ||||
|                     -- print('Initial Connection Made') | ||||
|                     curstate = STATE_INITIAL_CONNECTION_MADE | ||||
|                     zeldaSocket = client | ||||
|                     zeldaSocket:settimeout(0) | ||||
|                 end | ||||
|             end | ||||
|         end | ||||
|         emu.frameadvance() | ||||
|     end | ||||
| end | ||||
|  | ||||
| main() | ||||
							
								
								
									
										
											BIN
										
									
								
								data/lua/TLoZ/core.dll
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/lua/TLoZ/core.dll
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										380
									
								
								data/lua/TLoZ/json.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								data/lua/TLoZ/json.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,380 @@ | ||||
| -- | ||||
| -- json.lua | ||||
| -- | ||||
| -- Copyright (c) 2015 rxi | ||||
| -- | ||||
| -- This library is free software; you can redistribute it and/or modify it | ||||
| -- under the terms of the MIT license. See LICENSE for details. | ||||
| -- | ||||
|  | ||||
| local json = { _version = "0.1.0" } | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| -- Encode | ||||
| ------------------------------------------------------------------------------- | ||||
|  | ||||
| local encode | ||||
|  | ||||
| local escape_char_map = { | ||||
|   [ "\\" ] = "\\\\", | ||||
|   [ "\"" ] = "\\\"", | ||||
|   [ "\b" ] = "\\b", | ||||
|   [ "\f" ] = "\\f", | ||||
|   [ "\n" ] = "\\n", | ||||
|   [ "\r" ] = "\\r", | ||||
|   [ "\t" ] = "\\t", | ||||
| } | ||||
|  | ||||
| local escape_char_map_inv = { [ "\\/" ] = "/" } | ||||
| for k, v in pairs(escape_char_map) do | ||||
|   escape_char_map_inv[v] = k | ||||
| end | ||||
|  | ||||
|  | ||||
| local function escape_char(c) | ||||
|   return escape_char_map[c] or string.format("\\u%04x", c:byte()) | ||||
| end | ||||
|  | ||||
|  | ||||
| local function encode_nil(val) | ||||
|   return "null" | ||||
| end  | ||||
|  | ||||
|  | ||||
| local function encode_table(val, stack) | ||||
|   local res = {} | ||||
|   stack = stack or {} | ||||
|  | ||||
|   -- Circular reference? | ||||
|   if stack[val] then error("circular reference") end | ||||
|  | ||||
|   stack[val] = true | ||||
|  | ||||
|   if val[1] ~= nil or next(val) == nil then | ||||
|     -- Treat as array -- check keys are valid and it is not sparse | ||||
|     local n = 0 | ||||
|     for k in pairs(val) do | ||||
|       if type(k) ~= "number" then | ||||
|         error("invalid table: mixed or invalid key types") | ||||
|       end | ||||
|       n = n + 1 | ||||
|     end | ||||
|     if n ~= #val then | ||||
|       error("invalid table: sparse array") | ||||
|     end | ||||
|     -- Encode | ||||
|     for i, v in ipairs(val) do | ||||
|       table.insert(res, encode(v, stack)) | ||||
|     end | ||||
|     stack[val] = nil | ||||
|     return "[" .. table.concat(res, ",") .. "]" | ||||
|  | ||||
|   else | ||||
|     -- Treat as an object | ||||
|     for k, v in pairs(val) do | ||||
|       if type(k) ~= "string" then | ||||
|         error("invalid table: mixed or invalid key types") | ||||
|       end | ||||
|       table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) | ||||
|     end | ||||
|     stack[val] = nil | ||||
|     return "{" .. table.concat(res, ",") .. "}" | ||||
|   end | ||||
| end | ||||
|  | ||||
|  | ||||
| local function encode_string(val) | ||||
|   return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' | ||||
| end | ||||
|  | ||||
|  | ||||
| local function encode_number(val) | ||||
|   -- Check for NaN, -inf and inf | ||||
|   if val ~= val or val <= -math.huge or val >= math.huge then | ||||
|     error("unexpected number value '" .. tostring(val) .. "'") | ||||
|   end | ||||
|   return string.format("%.14g", val) | ||||
| end | ||||
|  | ||||
|  | ||||
| local type_func_map = { | ||||
|   [ "nil"     ] = encode_nil, | ||||
|   [ "table"   ] = encode_table, | ||||
|   [ "string"  ] = encode_string, | ||||
|   [ "number"  ] = encode_number, | ||||
|   [ "boolean" ] = tostring, | ||||
| } | ||||
|  | ||||
|  | ||||
| encode = function(val, stack) | ||||
|   local t = type(val) | ||||
|   local f = type_func_map[t] | ||||
|   if f then | ||||
|     return f(val, stack) | ||||
|   end | ||||
|   error("unexpected type '" .. t .. "'") | ||||
| end | ||||
|  | ||||
|  | ||||
| function json.encode(val) | ||||
|   return ( encode(val) ) | ||||
| end | ||||
|  | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| -- Decode | ||||
| ------------------------------------------------------------------------------- | ||||
|  | ||||
| local parse | ||||
|  | ||||
| local function create_set(...)  | ||||
|   local res = {} | ||||
|   for i = 1, select("#", ...) do | ||||
|     res[ select(i, ...) ] = true | ||||
|   end | ||||
|   return res | ||||
| end | ||||
|  | ||||
| local space_chars   = create_set(" ", "\t", "\r", "\n") | ||||
| local delim_chars   = create_set(" ", "\t", "\r", "\n", "]", "}", ",") | ||||
| local escape_chars  = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") | ||||
| local literals      = create_set("true", "false", "null") | ||||
|  | ||||
| local literal_map = { | ||||
|   [ "true"  ] = true, | ||||
|   [ "false" ] = false, | ||||
|   [ "null"  ] = nil, | ||||
| } | ||||
|  | ||||
|  | ||||
| local function next_char(str, idx, set, negate) | ||||
|   for i = idx, #str do | ||||
|     if set[str:sub(i, i)] ~= negate then | ||||
|       return i | ||||
|     end | ||||
|   end | ||||
|   return #str + 1 | ||||
| end | ||||
|  | ||||
|  | ||||
| local function decode_error(str, idx, msg) | ||||
|   --local line_count = 1 | ||||
|   --local col_count = 1 | ||||
|   --for i = 1, idx - 1 do | ||||
|   --  col_count = col_count + 1 | ||||
|   --  if str:sub(i, i) == "\n" then | ||||
|   --   line_count = line_count + 1 | ||||
|   --    col_count = 1 | ||||
|   --  end | ||||
|   -- end | ||||
|   -- emu.message( string.format("%s at line %d col %d", msg, line_count, col_count) ) | ||||
| end | ||||
|  | ||||
|  | ||||
| local function codepoint_to_utf8(n) | ||||
|   -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa | ||||
|   local f = math.floor | ||||
|   if n <= 0x7f then | ||||
|     return string.char(n) | ||||
|   elseif n <= 0x7ff then | ||||
|     return string.char(f(n / 64) + 192, n % 64 + 128) | ||||
|   elseif n <= 0xffff then | ||||
|     return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) | ||||
|   elseif n <= 0x10ffff then | ||||
|     return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, | ||||
|                        f(n % 4096 / 64) + 128, n % 64 + 128) | ||||
|   end | ||||
|   error( string.format("invalid unicode codepoint '%x'", n) ) | ||||
| end | ||||
|  | ||||
|  | ||||
| local function parse_unicode_escape(s) | ||||
|   local n1 = tonumber( s:sub(3, 6),  16 ) | ||||
|   local n2 = tonumber( s:sub(9, 12), 16 ) | ||||
|   -- Surrogate pair? | ||||
|   if n2 then | ||||
|     return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) | ||||
|   else | ||||
|     return codepoint_to_utf8(n1) | ||||
|   end | ||||
| end | ||||
|  | ||||
|  | ||||
| local function parse_string(str, i) | ||||
|   local has_unicode_escape = false | ||||
|   local has_surrogate_escape = false | ||||
|   local has_escape = false | ||||
|   local last | ||||
|   for j = i + 1, #str do | ||||
|     local x = str:byte(j) | ||||
|  | ||||
|     if x < 32 then | ||||
|       decode_error(str, j, "control character in string") | ||||
|     end | ||||
|  | ||||
|     if last == 92 then -- "\\" (escape char) | ||||
|       if x == 117 then -- "u" (unicode escape sequence) | ||||
|         local hex = str:sub(j + 1, j + 5) | ||||
|         if not hex:find("%x%x%x%x") then | ||||
|           decode_error(str, j, "invalid unicode escape in string") | ||||
|         end | ||||
|         if hex:find("^[dD][89aAbB]") then | ||||
|           has_surrogate_escape = true | ||||
|         else | ||||
|           has_unicode_escape = true | ||||
|         end | ||||
|       else | ||||
|         local c = string.char(x) | ||||
|         if not escape_chars[c] then | ||||
|           decode_error(str, j, "invalid escape char '" .. c .. "' in string") | ||||
|         end | ||||
|         has_escape = true | ||||
|       end | ||||
|       last = nil | ||||
|  | ||||
|     elseif x == 34 then -- '"' (end of string) | ||||
|       local s = str:sub(i + 1, j - 1) | ||||
|       if has_surrogate_escape then  | ||||
|         s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape) | ||||
|       end | ||||
|       if has_unicode_escape then  | ||||
|         s = s:gsub("\\u....", parse_unicode_escape) | ||||
|       end | ||||
|       if has_escape then | ||||
|         s = s:gsub("\\.", escape_char_map_inv) | ||||
|       end | ||||
|       return s, j + 1 | ||||
|      | ||||
|     else | ||||
|       last = x | ||||
|     end | ||||
|   end | ||||
|   decode_error(str, i, "expected closing quote for string") | ||||
| end | ||||
|  | ||||
|  | ||||
| local function parse_number(str, i) | ||||
|   local x = next_char(str, i, delim_chars) | ||||
|   local s = str:sub(i, x - 1) | ||||
|   local n = tonumber(s) | ||||
|   if not n then | ||||
|     decode_error(str, i, "invalid number '" .. s .. "'") | ||||
|   end | ||||
|   return n, x | ||||
| end | ||||
|  | ||||
|  | ||||
| local function parse_literal(str, i) | ||||
|   local x = next_char(str, i, delim_chars) | ||||
|   local word = str:sub(i, x - 1) | ||||
|   if not literals[word] then | ||||
|     decode_error(str, i, "invalid literal '" .. word .. "'") | ||||
|   end | ||||
|   return literal_map[word], x | ||||
| end | ||||
|  | ||||
|  | ||||
| local function parse_array(str, i) | ||||
|   local res = {} | ||||
|   local n = 1 | ||||
|   i = i + 1 | ||||
|   while 1 do | ||||
|     local x | ||||
|     i = next_char(str, i, space_chars, true) | ||||
|     -- Empty / end of array? | ||||
|     if str:sub(i, i) == "]" then  | ||||
|       i = i + 1 | ||||
|       break | ||||
|     end | ||||
|     -- Read token | ||||
|     x, i = parse(str, i) | ||||
|     res[n] = x | ||||
|     n = n + 1 | ||||
|     -- Next token  | ||||
|     i = next_char(str, i, space_chars, true) | ||||
|     local chr = str:sub(i, i) | ||||
|     i = i + 1 | ||||
|     if chr == "]" then break end | ||||
|     if chr ~= "," then decode_error(str, i, "expected ']' or ','") end | ||||
|   end | ||||
|   return res, i | ||||
| end | ||||
|  | ||||
|  | ||||
| local function parse_object(str, i) | ||||
|   local res = {} | ||||
|   i = i + 1 | ||||
|   while 1 do | ||||
|     local key, val | ||||
|     i = next_char(str, i, space_chars, true) | ||||
|     -- Empty / end of object? | ||||
|     if str:sub(i, i) == "}" then  | ||||
|       i = i + 1 | ||||
|       break | ||||
|     end | ||||
|     -- Read key | ||||
|     if str:sub(i, i) ~= '"' then | ||||
|       decode_error(str, i, "expected string for key") | ||||
|     end | ||||
|     key, i = parse(str, i) | ||||
|     -- Read ':' delimiter | ||||
|     i = next_char(str, i, space_chars, true) | ||||
|     if str:sub(i, i) ~= ":" then | ||||
|       decode_error(str, i, "expected ':' after key") | ||||
|     end | ||||
|     i = next_char(str, i + 1, space_chars, true) | ||||
|     -- Read value | ||||
|     val, i = parse(str, i) | ||||
|     -- Set | ||||
|     res[key] = val | ||||
|     -- Next token | ||||
|     i = next_char(str, i, space_chars, true) | ||||
|     local chr = str:sub(i, i) | ||||
|     i = i + 1 | ||||
|     if chr == "}" then break end | ||||
|     if chr ~= "," then decode_error(str, i, "expected '}' or ','") end | ||||
|   end | ||||
|   return res, i | ||||
| end | ||||
|  | ||||
|  | ||||
| local char_func_map = { | ||||
|   [ '"' ] = parse_string, | ||||
|   [ "0" ] = parse_number, | ||||
|   [ "1" ] = parse_number, | ||||
|   [ "2" ] = parse_number, | ||||
|   [ "3" ] = parse_number, | ||||
|   [ "4" ] = parse_number, | ||||
|   [ "5" ] = parse_number, | ||||
|   [ "6" ] = parse_number, | ||||
|   [ "7" ] = parse_number, | ||||
|   [ "8" ] = parse_number, | ||||
|   [ "9" ] = parse_number, | ||||
|   [ "-" ] = parse_number, | ||||
|   [ "t" ] = parse_literal, | ||||
|   [ "f" ] = parse_literal, | ||||
|   [ "n" ] = parse_literal, | ||||
|   [ "[" ] = parse_array, | ||||
|   [ "{" ] = parse_object, | ||||
| } | ||||
|  | ||||
|  | ||||
| parse = function(str, idx) | ||||
|   local chr = str:sub(idx, idx) | ||||
|   local f = char_func_map[chr] | ||||
|   if f then | ||||
|     return f(str, idx) | ||||
|   end | ||||
|   decode_error(str, idx, "unexpected character '" .. chr .. "'") | ||||
| end | ||||
|  | ||||
|  | ||||
| function json.decode(str) | ||||
|   if type(str) ~= "string" then | ||||
|     error("expected argument of type string, got " .. type(str)) | ||||
|   end | ||||
|   return ( parse(str, next_char(str, 1, space_chars, true)) ) | ||||
| end | ||||
|  | ||||
|  | ||||
| return json | ||||
							
								
								
									
										132
									
								
								data/lua/TLoZ/socket.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								data/lua/TLoZ/socket.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| ----------------------------------------------------------------------------- | ||||
| -- LuaSocket helper module | ||||
| -- Author: Diego Nehab | ||||
| -- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $ | ||||
| ----------------------------------------------------------------------------- | ||||
|  | ||||
| ----------------------------------------------------------------------------- | ||||
| -- Declare module and import dependencies | ||||
| ----------------------------------------------------------------------------- | ||||
| local base = _G | ||||
| local string = require("string") | ||||
| local math = require("math") | ||||
| local socket = require("socket.core") | ||||
| module("socket") | ||||
|  | ||||
| ----------------------------------------------------------------------------- | ||||
| -- Exported auxiliar functions | ||||
| ----------------------------------------------------------------------------- | ||||
| function connect(address, port, laddress, lport) | ||||
|     local sock, err = socket.tcp() | ||||
|     if not sock then return nil, err end | ||||
|     if laddress then | ||||
|         local res, err = sock:bind(laddress, lport, -1) | ||||
|         if not res then return nil, err end | ||||
|     end | ||||
|     local res, err = sock:connect(address, port) | ||||
|     if not res then return nil, err end | ||||
|     return sock | ||||
| end | ||||
|  | ||||
| function bind(host, port, backlog) | ||||
|     local sock, err = socket.tcp() | ||||
|     if not sock then return nil, err end | ||||
|     sock:setoption("reuseaddr", true) | ||||
|     local res, err = sock:bind(host, port) | ||||
|     if not res then return nil, err end | ||||
|     res, err = sock:listen(backlog) | ||||
|     if not res then return nil, err end | ||||
|     return sock | ||||
| end | ||||
|  | ||||
| try = newtry() | ||||
|  | ||||
| function choose(table) | ||||
|     return function(name, opt1, opt2) | ||||
|         if base.type(name) ~= "string" then | ||||
|             name, opt1, opt2 = "default", name, opt1 | ||||
|         end | ||||
|         local f = table[name or "nil"] | ||||
|         if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) | ||||
|         else return f(opt1, opt2) end | ||||
|     end | ||||
| end | ||||
|  | ||||
| ----------------------------------------------------------------------------- | ||||
| -- Socket sources and sinks, conforming to LTN12 | ||||
| ----------------------------------------------------------------------------- | ||||
| -- create namespaces inside LuaSocket namespace | ||||
| sourcet = {} | ||||
| sinkt = {} | ||||
|  | ||||
| BLOCKSIZE = 2048 | ||||
|  | ||||
| sinkt["close-when-done"] = function(sock) | ||||
|     return base.setmetatable({ | ||||
|         getfd = function() return sock:getfd() end, | ||||
|         dirty = function() return sock:dirty() end | ||||
|     }, { | ||||
|         __call = function(self, chunk, err) | ||||
|             if not chunk then | ||||
|                 sock:close() | ||||
|                 return 1 | ||||
|             else return sock:send(chunk) end | ||||
|         end | ||||
|     }) | ||||
| end | ||||
|  | ||||
| sinkt["keep-open"] = function(sock) | ||||
|     return base.setmetatable({ | ||||
|         getfd = function() return sock:getfd() end, | ||||
|         dirty = function() return sock:dirty() end | ||||
|     }, { | ||||
|         __call = function(self, chunk, err) | ||||
|             if chunk then return sock:send(chunk) | ||||
|             else return 1 end | ||||
|         end | ||||
|     }) | ||||
| end | ||||
|  | ||||
| sinkt["default"] = sinkt["keep-open"] | ||||
|  | ||||
| sink = choose(sinkt) | ||||
|  | ||||
| sourcet["by-length"] = function(sock, length) | ||||
|     return base.setmetatable({ | ||||
|         getfd = function() return sock:getfd() end, | ||||
|         dirty = function() return sock:dirty() end | ||||
|     }, { | ||||
|         __call = function() | ||||
|             if length <= 0 then return nil end | ||||
|             local size = math.min(socket.BLOCKSIZE, length) | ||||
|             local chunk, err = sock:receive(size) | ||||
|             if err then return nil, err end | ||||
|             length = length - string.len(chunk) | ||||
|             return chunk | ||||
|         end | ||||
|     }) | ||||
| end | ||||
|  | ||||
| sourcet["until-closed"] = function(sock) | ||||
|     local done | ||||
|     return base.setmetatable({ | ||||
|         getfd = function() return sock:getfd() end, | ||||
|         dirty = function() return sock:dirty() end | ||||
|     }, { | ||||
|         __call = function() | ||||
|             if done then return nil end | ||||
|             local chunk, err, partial = sock:receive(socket.BLOCKSIZE) | ||||
|             if not err then return chunk | ||||
|             elseif err == "closed" then | ||||
|                 sock:close() | ||||
|                 done = 1 | ||||
|                 return partial | ||||
|             else return nil, err end | ||||
|         end | ||||
|     }) | ||||
| end | ||||
|  | ||||
|  | ||||
| sourcet["default"] = sourcet["until-closed"] | ||||
|  | ||||
| source = choose(sourcet) | ||||
		Reference in New Issue
	
	Block a user
	 Rosalie-A
					Rosalie-A