diff --git a/cirnofarm.p64 b/cirnofarm.p64 index b8bbb07..402016a 100644 --- a/cirnofarm.p64 +++ b/cirnofarm.p64 @@ -6,78 +6,88 @@ version 2 :: sfx/ :: gfx/0.gfx b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTA0LTA0IDA3OjE5OjM0Iixtb2RpZmllZD0iMjAyNC0w -NC0yNiAxNTozNjo1NCIscmV2aXNpb249OTQ1XV1sejQA9ggAAKo_AADzFHtbMF09e2JtcD1weHUA -QyAQEATw8CxmbGFncz0wLHBhbl94CADaeT0wLHpvb209MTF9LDIA8QivGyPeA34D3iPeA-4JE24D -ThNuI04DfhMA-wOOAz4DTgMuI34jLgOeA-4UAy5eAB3wCfBFTYAtJi1QHWYdBSAdhg0VAB2WHRUN -pgQAww0VAA2WHQUQvQXwE1gAHzNYABXwKlBEgBRPHxRQDxSPHwRALxRPHyRADxQfH0QODxkEQA8U -PhoNDxkOBEANLgoACg0ODARADR4aAAoJDBQAQA5KCQoSAEEOKgAKJQASXiIA714NDA4EUBQ_DRSA -RPAVkwAdkfASoTABHxABHgIAEyAIAA8KAASuDxGhDSAODxwPDAQARA4RAA4MAAQEAPIQIQ4FAQAO -DB4PDA4LDA4RDA4BBQAOCw4LDB4LDgEMAR4AEwsCACABC0MAxQseDB4LDAsBDA4FEBwAYyEOASCu -INsAHzHbABb3BAY-GVBpLllOGQ4ZXlkeeVAeGbADACHADgMAXynAGfAXWwAPD7ECBQ8yAEf8Ac8T -LxEPE18RDxM-EQ8TDB8NACgMTw4AIywvDgC0HA8RfxMMLxE-Ex8gAAAbAB9PDQAO8QA8Di8RLgwt -jh0ODC0ObQ4HACcMXQgAfzwOLD0OHd7IAB3Z9AwPHzQedB40HiQeJAYA8QBEDiQe1B6EHiQOlB6E -DjQEAAYeABCEBAA-DvQJ0gId-0H-FgA2DoYOFgUWDgUmBTYONgUOZgUGDkYOFgVWDgb_AFYORg42 -BTYOJgUGDoYOBgUmDgYFZg5GDib_AIYOlgU2DhYFtg52BSYFFg42BZYOVoAAHvIAFQMMDxAMDhwO -DA4MbxUOCgBRHQwdDC0LALEdDgwdDA4tDhwOLQoAUgwtHC0MCgA0Dl0OCgA-DD0MDAACBSIABTYA -BUoAAl8AMy0MTXMAI20MCQA-DP0DxQAd0B8V1B6UDx8kHgQNZB0GAD8UHSQIAAQwdB40BABQDYQe -lA0SACZ0DSAAFzQGAH8EDRQNhB7UigAd4NQfFLQPHwQfFAQfH3QeCAABHgIsBB0IAJAUDiQeVB1U -HlQSAIUOZB0EHoQOBAYACiwAElQGAF4OZB3UHdACD-wDb-EUQF8UcB4PFT8fLkAeDxUOPx8PFQ4N -DiAeDQ4NPx8ODQ4NDhAGAGE8DQ4NHgALADENDhwHABE_BgAhHA4HABEeBgAnDhQaACUUDhoA4CwN -DixeTA4NTA0OAA48FgBSPA4QDixaAEEOIA4MCgBhDQ4cDkAuCgBPHnBeQMsAJAA-AFEuQA4PH0kA -Yw4NLiAOLAsAMRwOEMkAYA4NPAAOTAgAUUwuTC5MogAgFEy2ADccFBzOACwcHucANQ4AHhkACBoB -EiDUAG4NHkAuPC68AA-rAUHQE51QDX8QDVANDl8YDgcAQgw-HgwJAD8LHAsKABISOwgAEFwGAE5_ -DVCdaAgPhgAgEzddAD8HHAcKABIeN4UADz0BHg8yAIGQBgcGBx8WBgceCQCxBgcOFh4WHmYHBh4D -AAECAHUWHpYeFgcGAgByHgYHph4WDg4AAygAUlYeFh5WHAAGSQASphwAHwZIAAACKAA8Zh4WSQBf -Fg4mHobEAB32APUB3xgV3hXfFRXeFd4V3QYAf-UC3RXd9QFQAB1P9gANBgIAyi-2AWMBH0cPFg0O -AgA4FQ0OAgBz9QEPEy8MLzsJIB8MdQkZD3YJGQ9dCQ8OAAlxPwwPEy8RLxYANY8THyAAN28RDw4A -Pw8MXxAABAFAACEMPw4AH9_nAyAPMgD--0Vf-xQS0B4CAAYP0gYdT-8UEPA1ACEv0B4CAAcPTwAd -4PBIKLCIYIhgGNAY0AjgAgAQGAYAH2BMAR7DERSQFAAPFA8fBA4PjwmVDg0OBB4NBA0OAgDHBA0e -BK4EDh0ErQQtBAAbDQIAQx0kDQQCADk0DQQCAG8AFJAU8BHmAB47ERTAnAAoMA6aAIAgHgSeIB0E -bQsAWh0EbR4NDQAiBA0OAFINJA0EPRAAMA0ETUYAMcRABBMAIQRQEAAeIJoAD7YCbyIfFBwCD7kC -OD8SwC4CAAIf-psBHh7QngAP0gIrmXAI4AhwFEAIMI8CGgiPAhUIjwIwTghOkQJdTQhNBC0GABQN -AgAYCJkCGAiZAgMaACENAHMAX4AY0BhQ0gFPmGAI4AiAFDAIQOQAGghzAxUY4wAhPhjjACA9GNcA -DAYAA78AJwQY4gAXGOEAAxgAzw0AFDAYMBRwGNAYYJIBHS-w8OQCbC-_ELMAHk-Q-xQQywI5L-4S -TgAdWFA4sCjAAgBvmHB4oEjw8A0fj1AYAGhQ_BDw1AUeAO0BAgIArrA4kFhQiGB48Gh_AA-8AW9h -MDwPHCxgFRLwCAcMDhwwHA4MRwwODDAMDgxnDA4wHhcOAgAyDEAOCQDwISAeIFcADjAeIDcwHkAs -CBwQHkAHLAgcB0AeAAdcBwAeAB4QfBAeIAcMBwwHTHAXAAgAnwxwFxAXkBgQGH8BHvAFIAEsPxwB -UAEcbgFAAQwOEQ4RBw4KACIHDgMAAQwAMhwHHAoA8A8PF0cNDgEwARwnDSceATAxDAcOMVABBwwH -CAcOBwEKAP8mDggOCB4HATABBxxuBwEQARcsXhcBAAEXfAEXARARBwEXAQcRBxFAAQABBwEHAZAB -HAEcAUC2AR8PMgD-GP8ngA8b0B6ATxgOPUA9Am0wvTAdAm0CHSBdAl0gzTAdAk0CLTBNCF0wPQJt -QF0CLWB9cG2QTbAtJQYgDzIA-0f-AAQEBAAfHAAOFx4XDgAeADwAGv8GCAgEgAcwByAHEAfwBAcQ -ByAHMAeAQgAdkAgAOAAIAAgwCAYAgRAIABhQGFAYCgAEFgA-OAAINwEfDzIA5fAUEL8YIA60DgAO -1B5EPEQeJBw-HBwkHiQMXQwkHhQMDVwNDBQIADIMPQwKABEdAgAICgDRJAwdHB0MJB4kHD0cJEUA -n9QOAA60DiC_EL8AHw8yAP------------------------------------------llA9MTF9fQ== +NC0yNyAxNTo0MTowMCIscmV2aXNpb249MTAyNF1dbHo0AD0KAABDQAAA8xR7WzBdPXtibXA9cHh1 +AEMgEBAE8PAsZmxhZ3M9MCxwYW5feAgA2nk9MCx6b29tPTExfSwyAPEIrxsj3gN_A94j3gP_CRNu +A04TbiNOA34TAP8DjgM_A04DLiN_Iy4DngP_FAMuXgAd8AnwRU2ALSYtUB1mHQUgHYYNFQAdlh0V +DaYEAMMNFQANlh0FEL0F8BNYAB8zWAAV8CpQRIAUTx8UUA8Ujx8EQC8UTx8kQA8UHx9EDg8ZBEAP +FD4aDQ8ZDgRADS4KAAoNDgwEQA0eGgAKCQwUAEAOSgkKEgBBDioACiUAEl4iAO9eDQwOBFAUPg0U +gETwFZMAHbbwEqEwAa8QASABrgQArg8RoQ0gDg8cDwwEAEQOEQAODAAEBADyECEOBQEADgweDwwO +CwwOEQwOAQUADgsOCwweCw4BDAEeABMLAgAgAQtDAMULHgweCwwLAQwOBRAcAGMhDgEgriDDAB8x +wwAW9wQGPxlQaS5ZThkOGV5ZHnlQHhmwAwAhwA4DAF8pwBnwF1sAHmMSrxMwDhMCABMgCAAPCgAE +8Q8PEa4NIAOvGwMeAAOsAwUOAANMDxRMAw4FAAM8KzwRAAAIABMeDwCPEAOsAw4goyCdAg8PLQMt +-AHPEy8RDxNfEQ8TPxEPEwwfDQAoDE8OACMsLw4AtBwPEX8TDC8RPxMfIAAAGwAfTw0ADvEAPA4v +ES4MLY4dDgwtDm0OBwAnDF0IAH88Diw9Dh3eyAAd2fQMDx80HnQeNB4kHiQGAPEARA4kHtQehB4k +DpQehA40BAAGHgAQhAQAPw70CRwDHf9B-xYANg6GDhYFFg4FJgU2DjYFDmYFBg5GDhYFVg4G-gBW +DkYONgU2DiYFBg6GDgYFJg4GBWYORg4m-gCGDpYFNg4WBbYOdgUmBRYONgWWDlaAAB7yABUDDA8Q +DA4cDgwODG8VDgoAUR0MHQwtCwCxHQ4MHQwOLQ4cDi0KAFIMLRwtDAoANA5dDgoAPww9DAwAAgUi +AAU2AAVKAAJfADMtDE1zACNtDAkAPwz9A8UAHdAfFdQelA8fJB4EDWQdBgA-FB0kCAAEMHQeNAQA +UA2EHpQNEgAmdA0gABc0BgB-BA0UDYQe1IoAHeDUHxS0Dx8EHxQEHx90HggAAR4CLAQdCACQFA4k +HlQdVB5UEgCFDmQdBB6EDgQGAAosABJUBgBfDmQd1B3QAh3xAzYNFg8WbQ8SBg1GDxZNBg0PEhIA +YR0GPQ8SZhoAMyYNJhoAoQYNJg0GDxYNBk0cALA9Bh0PEn8WfxJtDioA8RQOJg0mDB0GHQYNDkYN +BgxtDhYNNgwGXQ5mDC0GLQ4GDUYMbRkAL358bgRP8RRAXxRwHg8VPx8uQB4PFQ4-Hw8VDg0OIB4N +Dg0-Hw4NDg0OEAYAYTwNDg0eAAsAMQ0OHAcAET4GACEcDgcAER4GACcOFBoAJRQOGgDgLA0OLF5M +Dg1MDQ4ADjwWAFI8DhAOLFoAQQ4gDgwKAGENDhwOQC4KAE8ecF5AywAkAD8AUS5ADg8fSQBjDg0u +IA4sCwAxHA4QyQBgDg08AA5MCABRTC5MLkyiACAUTLYANxwUHM4ALBwe5wA1DgAeGQAIGgESINQA +bg0eQC48LrwAD7kBQODwE51QDX8QDVANDl8YDgcAQgw-HgwJAD8LHAsKABISOwgAEFwGAG9_DVCd +8BOGADATN10APwccBwoAEh83hQAvH-AyAIGQBgcGBx8WBgceCQCxBgcOFh4WHmYHBh4DAAECAHUW +HpYeFgcGAgByHgYHph4WDg4AAygAUlYeFh5WHAAGSQASphwAHwZIAAACKAA8Zh4WSQBfFg4mHobE +AB32APUB3xgV3hXfFRXeFd4V3QYAf-UC3RXd9QFQAB1P9gANBgIAyi-2AWMBH0cPFg0OAgA4FQ0O +AgBz9QEPEy8ML60JIB8M5wkZD_gJGQ-PCQ8OAAlxPwwPEy8RLxYANY8THyAAN28RDw4APw8MXxAA +BAFAACEMPw4AH9_nA7QPMgAfTx8UsD4CAAofHuwIHX7-FBDwsP4QNwAP7QAVDzIA-xBf-xQS0B4C +AAYPsQEhDzUAIS-QHgIABw9PAB3g8EgosIhgiGAY0BjQCOACABAYBgAfYEgAHsMRFJAUAA8UDx8E +Dg_0CZUODQ4EHg0EDQ4CAMcEDR4ErgQOHQStBC0EABsNAgBDHSQNBAIAOTQNBAIAbwAUkBTwEZ4A +IBvAnAAoMA6aAIAgHgSeIB0EbQsAWh0EbR4NDQAiBA0OAFINJA0EPRAAMA0ETUYAMcRABBMAIQRQ +EAAeIJoAD2sEcQIcAg_5Ajg-EsAuAgACH-6bAR4e0J4AD9ICK5lwCOAIcBRACDCPAhoIjwIVCI8C +ME4ITpECXU0ITQQtBgAUDQIAGAiZAhgImQIDGgAhDQBzAF_AGNAYUAQCT5hgCOAIgBQwCEDkABoI +cwMVGOMAIT4Y4wAgPRjXAAwGAAO-ACcEGOIAFxjhAAMYAM8NABQwGDAUcBjQGGDgAE8P5AI8DxUH +IE-Q-xQQywI5L-4STgAdWFA4sCjAAgB-mHB4oEjwQEgAHZ-wUBgAaFD4EPC4AR4A7QECAgCvsDiQ +WFCIYHjwaH4AHqoB0xADvxsDEAO_BAD-DtMwBXAFYBUwFZAFEAXAFcAFEAWQFTAVYAVwBfADYgJP +YTA8DxwsYOAS8AgHDA4cMBwODEcMDgwwDA4MZwwOMB4XDgIAMgxADgkA8CEgHiBXAA4wHiA3MB5A +LAgcEB5ABywIHAdAHgAHXAcAHgAeEHwQHiAHDAcMB0xwFwAIAJ8McBcQF5AYEBjIDx7wBSABLD8c +AVABHG4BQAEMDhEOEQcOCgAiBw4DAAEMADIcBxwKAPAPDxdHDQ4BMAEcJw0nHgEwMQwHDjFQAQcM +BwgHDgcBCgD-Jg4IDggeBwEwAQccbgcBEAEXLF4XAQABF3wBFwEQEQcBFwEHEQcRQAEAAQcBBwGQ +ARwBHAFAtAMfDzIA-xj-KIAPG9AegE8YDj1APQJtML0wHQJtAh0gXQJdIM0wHQJNAi0wTQhdMD0C +bUBdAi1gfXBtkE2wLVCZAB8PMgD-R-8ABAQEAB8cAA4XHhcOAB4APAAa-wYICASABzAHIAcQB-AE +BxAHIAcwB4BCAB2QCAA4AAgACDAIBgCBEAgAGFAYUBgKAAQWAD84AAg3AR8PMgDl8BQQvxggDrQO +AA7UHkQ8RB4kHD8cHCQeJAxdDCQeFAwNXA0MFAgAMgw9DAoAER0CAAgKANEkDB0cHQwkHiQcPRwk +RQCf1A4ADrQOIL4QvwAfDzIA-0u3E58QUA4cDiwODA4IACMuDBAAAAgACBoAKjwuGAAiDB4KADFg +DgwiAI9wHkwOgF7wFIgAHvkPFVWABVYFcAVfEAVwBV4FcAUGPgYFcAUWHhYFcAVWBABPgFXwJUwB +Hw8yAP---------------------------------------9JQPTExfX0= :: gfx/.info.pod b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIixtb2RpZmllZD0iMjAyNC0w -NC0yNiAxNTozNjo1NCIsc3RvcmVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIl1dbHo0AAQAAAADAAAA +NC0yNyAxNTo0MTowMCIsc3RvcmVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIl1dbHo0AAQAAAADAAAA MG5pbA== :: map/0.map b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIixtb2RpZmllZD0iMjAyNC0w -NC0yNiAxNTozNjo1NCIscmV2aXNpb249OTgwXV1sejQArAMAABVCAADwCHt7Ym1wPXVzZXJkYXRh -KCJpMTYiLDMyAwAvIjABAP_DLjEwDAAPAQDVNTU4MAEAPzQ4MAEAXi80MAEACz8xMzABAHMXNAQA -DwEANQ9QACEfM4AAbg8BALoqNDgIAQ8EAwcOIAAPAQA2BIAAHzNgAQAvMzABAE4vNDgYAA8OJAAP -AQA5DmgADwEAch4zgAEPgABgDwEA---------7-1HSIpLGhpZGRlbj1mYWxzZSxuYW1lPSJvYmpl -Y3RzIixwYW5feD0tMTQwLjgzAQABFwBpeT0tMTA1FwCSdGlsZV9oPTE2CgAQdwoAj3pvb209MX0s -hBAJDwEA----mi9hMIAA7Q8BAP--------------Sw_EEAOvZm9yZWdyb3VuZIcQ4i8zOQQAAw8B -AFFjMzIwMDA5BAAaYgwAHzOAAF0aMAwAHzMAAW0WOIwBDwQAAQ_AADYPAQAFHmOUAAgIAB8zgABZ -FzAMAA_AAD0XNQQACAEALzUwAQAIDwABUB8wgADUHzUEAAwEAQAaNQQADwABtg_AAOxqMmIwMDJj -BAAEAQAvMmMEAAQfZIAANz8zNTABAB4vM2KAAGsvMDCAAPwfMoAAO2cyODAwMjkEAB9hAAFIL2Qw -XAAOACgADiQADwEALw5YAA9cAAMvMzIkAAEPAQAvD4AABgz8CA_AASUfM4AAIwF8CBc5BAAPpAEC -DwEALwGAABc1BAAPgAARAywALzMwAQAzDuACDzAADAMcAw4BAA_QAycPiAEDDxwCEw8BAAMPgABH -DZwBD4AA3Q4BAA_AAK4FCAMPBAAkD4cQMUdzb2xpghAvNTKCEAMfMYIQMg8BAP_ECEoCLzkwAQDW -LzFhBAADDwEAVS8xYQQAAy8wMJABUycxYgQAFjkEAC8wOAQACg8BADcfOAQAIg8BADcfOAQAIg_A -ATcfOAQAIw8BADUvMTgEACMPAQA1LzE4BAAjDwEANS8xOAQAIw8BADUvMTgEACMPAQA1LzFiBAAA -BAgEH2IEAAcPAQA2HzgEACIPAQA3HzgEACIPAQA3HzgEACIPAQA3HjgEAA_CEAIbOAQADwEANR44 -BAAfM6ILARs4BAAPAQA1HziAAP9sLjFieAQPgAABBBwADoAEDwEANB84BAASDwEARi8xOAQAEw8B -AEUvMTgEABMPAQBFLzE4BAATDwEARh84BAAiDwEANx84BAAiDwkhtkxiYWNrCSFOMTIuM4cQKDI2 -FgAPhxAGUG09MX19 +NC0yNyAxNTo0MTowMCIscmV2aXNpb249MTA2M11dbHo0AK8EAAARQgAA8Ah7e2JtcD11c2VyZGF0 +YSgiaTE2IiwzMgMALyIwAQD-gy4xMAwADwEA1TU1ODABAD80ODABAF4vNDABAAs-MTMwAQBzFzQE +AA8BADUPUAAhHzOAAG4PAQC6KjQ4CAEPBAMHDiAADwEANgSAAB8zYAEALzMwAQBOLzQ4GAAPDiQA +DwEAOQ5oAA8BAHIeM4ABD4AAYA8BAP_EBLQDDhAADwEAUwxwAAcQAB82xQhQLzYwAQDsB3ABDhAA +DwEAUAxwAAwQAA9oBB0IAQAISAAPgAQaD4AAQg8BAP8XDywLJi80MNABPx8zJQQYLzQwAQDU9R0i +KSxoaWRkZW49ZmFsc2UsbmFtZT0ib2JqZWN0cyIscGFuX3g9LTE2MS44MwEAARcAaXk9LTIwORcA +knRpbGVfaD0xNgoAEHcKAI96b29tPTF9LIQQCQ8BAP---5ovYTCAAO0PAQD--------------0sP +hBADpGZvcmVncm91bmSHEFQ3MS4xNgEAEzeFECk3NBUAD4MQsi8zOQQAAw8BAFFjMzIwMDA5BAAa +YgwALzMwgABcGjAMAB8zAAFtFjiMAQ8EAAEPgAA2DwEABR5jlAAICAAfM4AAWRcwDAAPgAA9FzUE +AAgBAC81MAEACA8AAVAfMIAA1B81BAAMBAEAGjUEAA8AAbYPgADsajJiMDAyYwQABAEALzJjBAAE +H2SAADc-MzUwAQAeEzOgBi8zOQQABw_AAEkAAQAqMjAQBSowORAAHzKAAKQiMmLgAQ_AABIPAQAI +AEAHLzJhgAAfUDMzMDAyPAAXOQQAHmGkAA-gAA4AyAIWZAwAHzOAAAUeZIAAHzMoAAANpAAPgAA0 +D9wCBQ_AAC4LAQAPgAAVLzAw-AgAHmGkAA_AAEkBPAEXOQQALWQwAQAPgAA2FzUEAA_AABEDLAAP +gAAVIzNlBAAfMAEAAw7gAg_MAwsEHAMMPAANNAMLBAAOAQAPiAEPLjMzoAMPHAIFDRgABwQALzAw +gAAPBFgGDQwDC6ADAZwBF2IEABtkGAATYQgABwwALzMwgAAPLjNjbAAOAQAPgABHDwEARw_AAI8m +MDCoAg8EAEEPgxARX3NvbGlkBSFXLzEwAQD-gB45DAAPuSXSH2EEAAMPAQBVLzFhBAADLzAwkAFT +JzFiBAAWOQQALzA4BAAKDwEANx84BAAiDwEANx84BAAiD4ABNx84BAAjDwEANS8xOAQAIw8BADUv +MTgEACMPAQA1LzE4BAAjDwEANS8xOAQAIw8BADUvMWIEAAAECAQfYgQABw8BADYfOAQAJA8BABUv +ZTABAAsfOAQAJA98ABUEBAAPAQAFHzgEACQXMFwADwQAAQQBAAwQAAwBAB44BAANTg0WYRwABQQA +CFwADwQAAQQBAC9lMAEACx44BAAfM6ILARs4BAAEXAAPBAAFBAEAL2UwAQALHziAAEQMBAAPAQAF +HziAAOsuMWJ4BA_AAAEEHAAJgAQfZQQAGA8BAA0fOAQAFA8BAAEvZTABAC4vMTgEABMPgAD-Ri8w +OAQAFB9lBAAgDwEAER84BAAUH2UEAB4PBSGSSmJhY2sFIQ_HECxQbT0xfX0= :: map/.info.pod b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIixtb2RpZmllZD0iMjAyNC0w -NC0yNiAxNTozNjo1NCIsc3RvcmVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIl1dbHo0AAQAAAADAAAA +NC0yNyAxNTo0MTowMCIsc3RvcmVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIl1dbHo0AAQAAAADAAAA MG5pbA== :: sfx/0.sfx b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTA0LTA0IDA3OjE5OjM0Iixtb2RpZmllZD0iMjAyNC0w -NC0yNiAxNTozNjo1NCIscmV2aXNpb249Nzg5XV1sejQARgEAAGYIAADwJ3B4dQADKAAAAwAED0AQ +NC0yNyAxNTo0MTowMCIscmV2aXNpb249ODUxXV1sejQARgEAAGYIAADwJ3B4dQADKAAAAwAED0AQ Ag4AAaABIAKgDgAPEAAN8MoBAgMEBQYHAA--kAgJCgsPDA8NDw4PDxAA8AANDxEPEg8TDxQPFQ8W DxcTAPEBDxgPGQ8aDxsPHA8dDx4PHxQA8QAgDyEPIg8jDyQPJQ8mDycUAPEAKA8pDyoPKw8sDy0P Lg8vFADxADAPMQ8yDzMPNA81DzYPNxQA-wU4DzkPOg87DzwPPQ8_Dz8AD--w-wEA6-InWgEQBg8g @@ -86,14 +96,14 @@ L-AAMAD--4If-wEAzPEd6A9AAA1ADxcACxoIBggQAgMQBQAGAAgASRcAJggJUP--CfEX-gn8F-4J 8HArAF-_sPBwBAgA-9wf-wEAl1D-----Hw== :: sfx/.info.pod b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIixtb2RpZmllZD0iMjAyNC0w -NC0yNiAxNTozNjo1NCIsc3RvcmVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIl1dbHo0AAQAAAADAAAA +NC0yNyAxNTo0MTowMCIsc3RvcmVkPSIyMDI0LTA0LTA0IDA3OjE5OjMzIl1dbHo0AAQAAAADAAAA MG5pbA== :: main.lua ---[[pod_format="raw",created="2024-04-04 07:19:33",modified="2024-04-26 15:36:54",revision=1065]] +--[[pod_format="raw",created="2024-04-04 07:19:33",modified="2024-04-27 15:41:00",revision=1138]] include("/cirnofarm/src/game.lua") :: .info.pod b64$LS1bW3BvZCxjcmVhdGVkPSIyMDI0LTA0LTE2IDE5OjQyOjIyIixtb2RpZmllZD0iMjAyNC0w -NC0yNiAxNTozNjo1NCIscnVudGltZT02LHN0b3JlZD0iMjAyNC0wNC0xNiAxOTo0MDowNSIsd29y +NC0yNyAxNTo0MTowMCIscnVudGltZT02LHN0b3JlZD0iMjAyNC0wNC0xNiAxOTo0MDowNSIsd29y a3NwYWNlcz17e2xvY2F0aW9uPSJtYWluLmx1YSMxIix3b3Jrc3BhY2VfaW5kZXg9MX0se2xvY2F0 aW9uPSJnZngvMC5nZngiLHdvcmtzcGFjZV9pbmRleD0yfSx7bG9jYXRpb249Im1hcC8wLm1hcCIs d29ya3NwYWNlX2luZGV4PTN9LHtsb2NhdGlvbj0ic2Z4LzAuc2Z4Iix3b3Jrc3BhY2VfaW5kZXg9 diff --git a/src/error_explorer.lua b/src/error_explorer.lua new file mode 100644 index 0000000..77bb535 --- /dev/null +++ b/src/error_explorer.lua @@ -0,0 +1,561 @@ +-- # picotron error explorer +-- +-- by kira +-- +-- version 0.0.4 +-- +-- an interactive error screen for picotron. +-- on error, shows the stack, local variables, +-- and the source code when available. +-- +-- ## usage +-- +-- `include` or `require` `error_explorer.lua` +-- in your program _after_ defining your `_init`, +-- `_update`, and `_draw` functions. +-- +-- press `up` and `down` to move up and down on +-- the stack, press `x` or `space` to toggle font +-- size. click on tables in the variable view to +-- expand them. +-- +-- ## how it works +-- +-- in order to catch errors and inspect runtime +-- state, this script replaces `_init`, `_update` +-- and `_draw` functions with ones that call the +-- original ones inside a coroutine. +-- +-- when there's an error, it uses lua's debug +-- library to inspect the coroutine. a copy +-- of the error is also printed to the console +-- with printh if you're running picotron from +-- the command line. +-- +-- the following debug apis are used: +-- +-- - `debug.getinfo` +-- - `debug.getlocal` +-- - `debug.getupvalue` +-- - `debug.traceback` +-- +-- ## version history +-- +-- version 0.0.4 +-- +-- - also catch errors in `_init` +-- +-- version 0.0.3 +-- +-- - automatically choose the right stack frame +-- based on the error message +-- - more thoroughly protect from errors in error +-- explorer itself +-- +-- version 0.0.2 +-- +-- - don't regenerate stack info every draw +-- - scroll stack and variables list with mousewheel +-- - click on stack to switch stack frames +-- - click on tables in variables view to expand them +-- - escape strings when printing them +-- +-- version 0.0.1 +-- +-- - adjust colors +-- - code cleanup +-- - use `btnp` instead of `keyp` +-- - slightly more thorough `reset` +-- - don't show temporaries +-- +-- version 0.0.0 (prerelease) +-- +-- - initial discord beta + +-- ## license +-- +-- Copyright 2024 Kira Boom +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the “Software”), to +-- deal in the Software without restriction, including without limitation the +-- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +-- sell copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +-- DEALINGS IN THE SOFTWARE. + +---- util ---------------------------------------- + +local function filename_of (path) + return path:match ('[^/]*$') +end + +local function safe_tostring (value) + if type (value) == 'string' then + return string.format ('%q', value) + else + local success, value_string = pcall (tostring, value) + return success and value_string + or ('error during tostring: ' .. tostring (value_string)) + end +end + +local function get_lines (text) + local lines = {} + for line in text:gmatch ("(.-)\r?\n") do + table.insert (lines, line) + end + local last_line = text:match ('([^\n]*)$') + if last_line and last_line ~= '' then + table.insert (lines, last_line) + end + return lines +end + +local function compare_keys (a, b) + local ta = type (a.key) + local tb = type (b.key) + if ta ~= tb then + return ta < tb + end + if ta == 'number' or ta == 'string' then + return a.key < b.key + else + return safe_tostring (a.key) < safe_tostring (b.key) + end +end + +local function sort (t, f) + -- insertion sort + f = f or function (a, b) return a < b end + for i = 1, #t-1 do + local val = t[i+1] + local j = i + while j >= 1 and not f(t[j], val) do + t[j+1] = t[j] + j = j - 1 + end + t[j+1] = val + end +end + +local function approach (from, to) + return from + (to - from) * 0.25 +end + +local function round (value) + return math.floor (value + 0.5) +end + +local function parse_message_for_location (msg) + local path, line, err = msg:match ('^([^:]+):(%d+):(.*)$') + return path, tonumber (line), err +end + +---- state --------------------------------------- + +local _G = _G +local error_message +local error_thread +local error_traceback +local init_done = false +local use_small_font = false +local mouse_was_clicked = false + +-- stack view +local stack_frames = {} +local current_stack_index = 1 +local hovered_stack_index = false +local mouse_over_stack = false +local stack_max_scroll = 0 +local stack_scroll = 0 +local stack_scroll_smooth = 0 + +-- variables view +local variables = {} +local hovered_variable = false +local variables_max_scroll = 0 +local variables_scroll = 0 +local variables_scroll_smooth = 0 +local mouse_over_variables = false + +-- source view +local source_lines = {} + + +---- main events --------------------------------- + +local W = 480 +local H = 270 + +local function rebuild () + -- rebuild stack frame info + stack_frames = {} + variables = {} + source_lines = {} + variables_scroll = 0 + variables_scroll_smooth = 0 + + for i = 0, 20 do + local info = debug.getinfo (error_thread, i) + if not info then + break + end + + if info.short_src then + table.insert (stack_frames, { + filename = filename_of (info.short_src), + path = info.short_src, + line = info.currentline, + depth = i, + fn_name = (info.name or (filename_of (info.short_src) .. ':' .. tostring (info.linedefined))), + source = info.source, + }) + end + end + + local frame = stack_frames [current_stack_index] + + if not frame then + return + end + + -- rebuild variables + do + local local_index = 1 + repeat + local name, value = debug.getlocal (error_thread, frame.depth, local_index) + if name then + if name ~= '(temporary)' then + table.insert (variables, { + key = name, + value = value, + }) + end + local_index = local_index + 1 + end + until not name + + local info = debug.getinfo (error_thread, frame.depth) + if info and info.func then + local upvalue_index = 1 + repeat + local name, value = debug.getupvalue (info.func, upvalue_index) + if name then + table.insert (variables, { + key = name, + value = value, + }) + upvalue_index = upvalue_index + 1 + end + until not name + end + end + + -- rebuild source lines + local source = frame.source + if source then + if string.sub (source, 1, 1) == '@' then + local filename = string.sub (source, 2, #source) + source = fetch (filename) + end + if source and type (source) == 'string' then + source_lines = get_lines (source) + end + end +end + +local function error_update () + local last_index = current_stack_index + if btnp (5) or keyp 'space' then + use_small_font = not use_small_font + end + if btnp (2) then + current_stack_index = math.max (1, current_stack_index - 1) + stack_scroll = math.min (current_stack_index-1, stack_scroll) + end + if btnp (3) then + current_stack_index = math.min (#stack_frames, current_stack_index + 1) + stack_scroll = math.max ((current_stack_index) - (#stack_frames - stack_max_scroll), stack_scroll) + end + + local _, _, click, _, wheel = mouse () + if mouse_over_stack then + stack_scroll = math.max (0, math.min (stack_scroll - wheel * 2, stack_max_scroll)) + end + stack_scroll_smooth = approach (stack_scroll_smooth, stack_scroll) + if mouse_over_variables then + variables_scroll = math.max (0, math.min (variables_scroll - wheel * 2, variables_max_scroll)) + end + variables_scroll_smooth = approach (variables_scroll_smooth, variables_scroll) + + click = click ~= 0 + if click and not mouse_was_clicked then + if hovered_stack_index then + current_stack_index = hovered_stack_index + end + if hovered_variable and type (hovered_variable.value) == 'table' then + if hovered_variable.contents then + hovered_variable.contents = nil + else + local contents = {} + hovered_variable.contents = contents + for k,v in pairs (hovered_variable.value) do + table.insert (contents, { + key = k, + value = v, + }) + end + sort (contents, compare_keys) + end + end + end + mouse_was_clicked = click + + if current_stack_index ~= last_index then + rebuild() + end +end + +local function error_draw () + local prefix = use_small_font and '\014' or '' + local font_height = (use_small_font and 6 or 11) + local mx, my = mouse() + local over_section = false + local x0, y0, x, y + + local function go_to (new_x, new_y) + x0, y0 = new_x, new_y + x, y = x0, y0 + end + + local function section (sx, sy, sw, sh) + over_section = + mx >= sx and mx < sx + sw and + my >= sy and my < sy + sh + clip (sx, sy, sw, sh) + go_to(sx+2, sy+2) + end + + local function print_horizontal (text, color) + local new_x, _new_y = print (prefix .. text, x, y, color) + x = new_x + end + + local function print_line (text, color) + local _new_x, new_y = print (prefix .. text, x, y, color) + x = x0 + y = new_y + end + + -- draw setup + cls (0) + -- lighter dark gray for readability + pal (5, 0xff707070, 2) + color (5) + + -- error message + section (0, 0, W, H/2) + mouse_over_stack = over_section + + local loc_path, loc_line, err = parse_message_for_location (error_message) + if loc_path then + print_line ('error at ' .. loc_path .. ':' .. loc_line .. ':', 6) + print_line (' ' .. err, 8) + else + print_line ('error:', 6) + print_line (' ' .. error_message, 8) + end + + -- stack frames + print_line ('stack:', 6) + section (0, y, W, H/2-y) + local stack_top_y = y + y = y - round (stack_scroll_smooth * font_height) + local last_hovered_stack_index = hovered_stack_index + hovered_stack_index = false + for i, frame in ipairs (stack_frames) do + color (last_hovered_stack_index == i and 7 or + current_stack_index == i and 6 or 5) + + local y_before = y + print_line (string.format (' %s:%d in function %s', + frame.filename, frame.line, frame.fn_name )) + if over_section then + if my >= y_before and my < y then + hovered_stack_index = i + end + end + end + stack_max_scroll = #stack_frames - (H/2 - stack_top_y) / font_height + + local frame = stack_frames [current_stack_index] + if not frame then + return + end + + -- variables + section (0, H/2, W/2, H/2) + mouse_over_variables = over_section + print_line ('variables:', 6) + section (0, y, W/2, H-y) + local variables_top_y = y + y = y - round (variables_scroll_smooth * font_height) + local last_hovered_variable = hovered_variable + hovered_variable = false + local variable_count = 0 + local function draw_variable (variable, indent) + variable_count = variable_count + 1 + local hovered = variable == last_hovered_variable + local y_before = y + print_horizontal (indent .. variable.key, hovered and 7 or 6) + print_horizontal (': ', variable == last_hovered_variable and 7 or 5) + print_line (safe_tostring(variable.value)) + + if over_section and type (variable.value) == 'table' then + if mx >= 0 and mx < W/2 and my >= y_before and my < y then + hovered_variable = variable + end + end + + if variable.contents then + for _, v in ipairs (variable.contents) do + draw_variable (v, indent .. ' ') + end + end + end + for _, variable in ipairs (variables) do + draw_variable (variable, ' ') + end + variables_max_scroll = variable_count - (H - variables_top_y) / font_height + + -- source + section (W/2, H/2, W/2, H/2) + print_line ('source of ' .. frame.path .. ':', 6) + local context = use_small_font and 10 or 5 + local i_min = math.max (1, frame.line - context) + local i_max = math.min (#source_lines, frame.line + context) + for i = i_min, i_max do + color (i == frame.line and 6 or 5) + print_horizontal (string.format ('%4d ', i)) + print_line (source_lines [i]) + end + + clip () +end + +---- taking over during errors ------------------- + +local function reset () + -- based on reset() from /system/lib/head.lua + -- see that fn for info + note () + -- picotron segfaults if we call clip() during init + if init_done then + clip () + end + camera () + pal () + palt () + memset (0x551f, 0, 9) + poke (0x5508, 0x3f) + poke (0x5509, 0x3f) + poke (0x550a, 0x3f) + poke (0x550b, 0x00) + color (6) + fillp () + poke (0x5f56, 0x40) + poke (0x5f57, 0x56) + poke (0x4000, get (fetch"/system/fonts/lil.font")) + poke (0x5600, get (fetch"/system/fonts/p8.font")) + poke (0x5606, peek (0x5600) * 4) + poke (0x5605, 0x2) + poke (0x5f28, 64) + poke (0x5f29, 64) +end + +local function on_error (thread, message) + -- do this first in case we hit another error + error_traceback = debug.traceback (thread, message) + printh (error_traceback) + + error_thread = thread + error_message = tostring (message) + reset () + rebuild () + -- jump to the proper stack frame if we can + local loc_path, loc_line = parse_message_for_location (error_message) + for i, frame in ipairs (stack_frames) do + if frame.path == loc_path and frame.line == loc_line then + current_stack_index = i + rebuild () + break + end + end +end + +---- install main events that catch errors ------- + +local user_init = rawget (_G, '_init') +local user_update = rawget (_G, '_update') +local user_draw = rawget (_G, '_draw') + +assert (user_draw and user_update, + 'please include install_error_handler after defining both _update and _draw') + +local function call_error_event (fn, ...) + -- if there's an error in our update or draw, throw the + -- original error as well as the new error + local success, err = pcall (fn, ...) + if not success then + error (error_traceback .. '\n\nerror during error handling: ' .. tostring (err)) + end +end + +local function call_protected (fn) + -- need to use coresume etc. and not coroutine.resume etc. + -- for picotron compatibility + local thread = cocreate (fn) + local success, message = coresume(thread) + if costatus (thread) ~= 'dead' then + call_error_event (on_error, thread, 'setup_error_display.lua: _update and _draw shouldn\'t yield') + end + if not success then + call_error_event (on_error, thread, message) + end +end + +if user_init then + function _init () + call_protected (user_init) + init_done = true + end +else + init_done = true +end + +function _update () + if error_thread then + call_error_event (error_update) + else + call_protected (user_update) + end +end + +function _draw () + if error_thread then + call_error_event (error_draw) + else + call_protected (user_draw) + end +end diff --git a/src/game.lua b/src/game.lua index be7ec39..343bad8 100644 --- a/src/game.lua +++ b/src/game.lua @@ -108,18 +108,30 @@ particles = {} function _draw() cls(0) - + camera(cirnoInstance.x - 240, cirnoInstance.y - 135) foreach(LAYERS, render_layer) - mouse_debug.draw(4, tile_width, tile_height) + local mx,my = mouse() + camera() + mouse_debug.draw(mx,my,4, tile_width, tile_height) + -- Draw UI + draw_ui() weapons_manager.debug_draw() + +end + +function draw_ui() print(string.format("Actors: %d", count(actors)),0,32+8) print(string.format("%.4f %dfps",stat(1),stat(7)),2,16,5) + + end +include(make_path("error_explorer") .. ".lua") + function spawn_objects() local width = 32 @@ -149,7 +161,6 @@ function spawn_objects() end - end function get_actor_from_sprite(sprite) diff --git a/src/mouse_debug.lua b/src/mouse_debug.lua index 0ccf60c..cf97a2b 100644 --- a/src/mouse_debug.lua +++ b/src/mouse_debug.lua @@ -2,8 +2,8 @@ local map_manager = require(make_path("map")) M = {} -function debug_mouse(layers_count, tile_width, tile_height) - local mx,my = mouse() +function debug_mouse(mx,my,layers_count, tile_width, tile_height) + --local mx,my = mouse() local x_offset = 5 local y_offset = 5 @@ -36,8 +36,8 @@ function debug_mouse(layers_count, tile_width, tile_height) end end -function M.draw(layers_count, tile_width, tile_height) - return debug_mouse(layers_count, tile_width, tile_height) +function M.draw(mx,my,layers_count, tile_width, tile_height) + return debug_mouse(mx,my,layers_count, tile_width, tile_height) end return M \ No newline at end of file