From 18789a9d9c58f8883f32a3e59351455ceb7d5459 Mon Sep 17 00:00:00 2001 From: boxgaming <75969133+boxgaming@users.noreply.github.com> Date: Wed, 16 Feb 2022 11:40:03 -0600 Subject: [PATCH] initial revision --- .gitignore | 2 + codemirror/qb-ide.css | 48 + codemirror/qb-lang.js | 372 +++++ gx/__gx_font_default.png | Bin 0 -> 7454 bytes gx/__gx_font_default_black.png | Bin 0 -> 6667 bytes gx/gx.js | 2567 ++++++++++++++++++++++++++++++++ img/fullscreen.png | Bin 0 -> 6250 bytes img/gx-logo.png | Bin 0 -> 1696 bytes img/play.png | Bin 0 -> 533 bytes index.html | 243 +++ lp-dosvga.ttf | Bin 0 -> 79584 bytes qb.js | 922 ++++++++++++ qb2js.js | 2134 ++++++++++++++++++++++++++ tools/qb2js.bas | 2365 +++++++++++++++++++++++++++++ tools/webserver.bas | 360 +++++ util/shorty.min.js | 2 + 16 files changed, 9015 insertions(+) create mode 100644 .gitignore create mode 100644 codemirror/qb-ide.css create mode 100644 codemirror/qb-lang.js create mode 100644 gx/__gx_font_default.png create mode 100644 gx/__gx_font_default_black.png create mode 100644 gx/gx.js create mode 100644 img/fullscreen.png create mode 100644 img/gx-logo.png create mode 100644 img/play.png create mode 100644 index.html create mode 100644 lp-dosvga.ttf create mode 100644 qb.js create mode 100644 qb2js.js create mode 100644 tools/qb2js.bas create mode 100644 tools/webserver.bas create mode 100644 util/shorty.min.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..499df46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +tools/webserver.exe +tools/qb2js.exe diff --git a/codemirror/qb-ide.css b/codemirror/qb-ide.css new file mode 100644 index 0000000..ba2351d --- /dev/null +++ b/codemirror/qb-ide.css @@ -0,0 +1,48 @@ +@font-face { + font-family: dosvga; + src: url(lp-dosvga.ttf); +} + +.cm-s-lesser-dark { + line-height: 1em; +} +.cm-s-lesser-dark.CodeMirror { background: rgb(0, 0, 39); color: rgb(216, 216, 216); text-shadow: 0 -1px 1px #262626; font-family: dosvga; /*letter-spacing: -1px*/} +.cm-s-lesser-dark div.CodeMirror-selected { background: #45443B; } /* 33322B*/ +.cm-s-lesser-dark .CodeMirror-line::selection, .cm-s-lesser-dark .CodeMirror-line > span::selection, .cm-s-lesser-dark .CodeMirror-line > span > span::selection { background: rgba(0, 49, 78, .99); } +.cm-s-lesser-dark .CodeMirror-line::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span::-moz-selection, .cm-s-lesser-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(69, 68, 59, .99); } +.cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid #fff; } +.cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/ + +.cm-s-lesser-dark.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/ + +.cm-s-lesser-dark .CodeMirror-gutters { background: rgb(0, 49, 78); border-right:1px solid #aaa; } +.cm-s-lesser-dark .CodeMirror-guttermarker { color: #599eff; } +.cm-s-lesser-dark .CodeMirror-guttermarker-subtle { color: #777; } +.cm-s-lesser-dark .CodeMirror-linenumber { color: rgb(175, 175, 175); } + +.cm-s-lesser-dark span.cm-header { color: #a0a; } +.cm-s-lesser-dark span.cm-quote { color: #090; } +.cm-s-lesser-dark span.cm-keyword { color: rgb(69, 118, 147); } +.cm-s-lesser-dark span.cm-atom { color: #C2B470; } +.cm-s-lesser-dark span.cm-number { color: rgb(216, 98, 78); } +.cm-s-lesser-dark span.cm-def { color: white; } +.cm-s-lesser-dark span.cm-variable { color:rgb(216, 216, 216); } +.cm-s-lesser-dark span.cm-variable-2 { color: #669199; } +.cm-s-lesser-dark span.cm-variable-3, .cm-s-lesser-dark span.cm-type { color: white; } +.cm-s-lesser-dark span.cm-property { color: #92A75C; } +.cm-s-lesser-dark span.cm-operator { color: #92A75C; } +.cm-s-lesser-dark span.cm-comment { color: rgb(98, 98, 98); } +.cm-s-lesser-dark span.cm-string { color: rgb(255, 167, 0); } +.cm-s-lesser-dark span.cm-string-2 { color: #f50; } +.cm-s-lesser-dark span.cm-meta { color: #738C73; } +.cm-s-lesser-dark span.cm-qualifier { color: #555; } +.cm-s-lesser-dark span.cm-builtin { color: rgb(69, 118, 147); } +.cm-s-lesser-dark span.cm-bracket { color: #EBEFE7; } +.cm-s-lesser-dark span.cm-tag { color: #669199; } +.cm-s-lesser-dark span.cm-attribute { color: #81a4d5; } +.cm-s-lesser-dark span.cm-hr { color: #999; } +.cm-s-lesser-dark span.cm-link { color: #7070E6; } +.cm-s-lesser-dark span.cm-error { color: #9d1e15; } + +.cm-s-lesser-dark .CodeMirror-activeline-background { background: rgb(0, 49, 78); } +.cm-s-lesser-dark .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; } diff --git a/codemirror/qb-lang.js b/codemirror/qb-lang.js new file mode 100644 index 0000000..7fe4343 --- /dev/null +++ b/codemirror/qb-lang.js @@ -0,0 +1,372 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: https://codemirror.net/LICENSE + +/* +For extra ASP classic objects, initialize CodeMirror instance with this option: + isASP: true + +E.G.: + var editor = CodeMirror.fromTextArea(document.getElementById("code"), { + lineNumbers: true, + isASP: true + }); +*/ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("vbscript", function(conf, parserConf) { + var ERRORCLASS = 'error'; + + function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b", "i"); + } + + var singleOperators = new RegExp("^[\\+\\-\\*/&\\\\\\^<>=]"); + var doubleOperators = new RegExp("^((<>)|(<=)|(>=))"); + var singleDelimiters = new RegExp('^[\\.,]'); + var brakets = new RegExp('^[\\(\\)]'); + var identifiers = new RegExp("^[A-Za-z][_A-Za-z0-9]*"); + + var openingKeywords = ['sub','select','while','if','function', 'property', 'with', 'for', 'type']; + var middleKeywords = ['else','elseif','case']; + var endKeywords = ['next','loop','wend']; + + var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'is', 'mod', 'eqv', 'imp']); + var commonkeywords = ['dim', 'as', 'redim', 'then', 'until', 'randomize', + 'byval','byref','new','property', 'exit', 'in', + 'const', 'integer', 'single', 'double', '_unsigned', 'string', '_byte', + 'let', 'option explicit', 'call']; + + //This list was from: http://msdn.microsoft.com/en-us/library/f8tbc79x(v=vs.84).aspx + var atomWords = ['true', 'false', 'nothing', 'empty', 'null']; + + // QB64 Keywords + var builtinFuncsWords = ['_delay', '_fontwidth', '_height', '_limit', '_keyhit', '_newimage', '_pi', '_printstring', '_printwidth', '_rgb', + '_rgb32', '_round', '_title', '_trim', '_width', 'abs', 'asc', 'atn', 'chr\\$', 'cls', 'color', 'cos', 'fix', 'input', + 'instr', 'int', 'left\\$', 'lcase\\$', 'len', 'line', 'locate', 'ltrim\\$', 'mid\\$', 'print', 'right\\$', 'rtrim\\$', 'rnd', + 'screen', 'sgn', 'sin', 'sleep', 'sqr', 'str\\$', 'tan', 'ubound', 'ucase\\$', 'val', '_screenexists', 'exp', 'timer', + 'pset', '_red32', '_green32', '_blue32', '_red', '_green', '_blue', 'circle', '_alpha32', '_keydown', 'swap', '_instrrev', + 'command\\$', '_mouseinput', '_mousex', '_mousey', '_mousebutton', 'log', '_atan2', 'inkey\\$']; + + //This list was from: http://msdn.microsoft.com/en-us/library/ydz4cfk3(v=vs.84).aspx + var builtinConsts = ['gx_true','vbBlack', 'vbRed', 'vbGreen', 'vbYellow', 'vbBlue', 'vbMagenta', 'vbCyan', 'vbWhite', 'vbBinaryCompare', 'vbTextCompare', + 'vbSunday', 'vbMonday', 'vbTuesday', 'vbWednesday', 'vbThursday', 'vbFriday', 'vbSaturday', 'vbUseSystemDayOfWeek', 'vbFirstJan1', 'vbFirstFourDays', 'vbFirstFullWeek', + 'vbGeneralDate', 'vbLongDate', 'vbShortDate', 'vbLongTime', 'vbShortTime', 'vbObjectError', + 'vbOKOnly', 'vbOKCancel', 'vbAbortRetryIgnore', 'vbYesNoCancel', 'vbYesNo', 'vbRetryCancel', 'vbCritical', 'vbQuestion', 'vbExclamation', 'vbInformation', 'vbDefaultButton1', 'vbDefaultButton2', + 'vbDefaultButton3', 'vbDefaultButton4', 'vbApplicationModal', 'vbSystemModal', 'vbOK', 'vbCancel', 'vbAbort', 'vbRetry', 'vbIgnore', 'vbYes', 'vbNo', + 'vbCr', 'VbCrLf', 'vbFormFeed', 'vbLf', 'vbNewLine', 'vbNullChar', 'vbNullString', 'vbTab', 'vbVerticalTab', 'vbUseDefault', 'vbTrue', 'vbFalse', + 'vbEmpty', 'vbNull', 'vbInteger', 'vbLong', 'vbSingle', 'vbDouble', 'vbCurrency', 'vbDate', 'vbString', 'vbObject', 'vbError', 'vbBoolean', 'vbVariant', 'vbDataObject', 'vbDecimal', 'vbByte', 'vbArray']; + //This list was from: http://msdn.microsoft.com/en-us/library/hkc375ea(v=vs.84).aspx + var builtinObjsWords = ['WScript', 'err', 'debug', 'RegExp', '\\$if', '\\$end if']; + var knownProperties = ['description', 'firstindex', 'global', 'helpcontext', 'helpfile', 'ignorecase', 'length', 'number', 'pattern', 'source', 'value', 'count']; + //var knownMethods = ['clear', 'execute', 'raise', 'replace', 'test', 'write', 'writeline', 'close', 'open', 'state', 'eof', 'update', 'addnew', 'end', 'createobject', 'quit', 'gxscenecreate']; + var knownMethods = ['gxongameevent', 'gxmousex', 'gxmousey', 'gxsoundload', 'gxsoundplay', 'gxsoundrepeat', 'gxsoundvolume', 'gxsoundpause', 'gxsoundstop', + 'gxsoundmuted', 'gxsoundmuted', 'gxentityanimate', 'gxentityanimatestop', 'gxentityanimatemode', 'gxentityanimatemode', + 'gxscreenentitycreate', 'gxentitycreate', 'gxentityvisible', 'gxentitymove', 'gxentitypos', 'gxentityvx', 'gxentityvy', + 'gxentityx', 'gxentityy', 'gxentitywidth', 'gxentityheight', 'gxentityframenext', 'gxentityframeset', 'gxentitytype', + 'gxentitytype', 'gxentityuid', 'gxfontuid', 'gxentityapplygravity', 'gxentitycollisionoffset', + 'gxentitycollisionoffsetleft', 'gxentitycollisionoffsettop', 'gxentitycollisionoffsetright', + 'gxentitycollisionoffsetbottom', 'gxfullscreen', 'gxbackgroundadd', 'gxbackgroundy', 'gxbackgroundheight', + 'gxbackgroundclear', 'gxsceneembedded', 'gxscenecreate', 'gxscenewindowsize', 'gxscenescale', 'gxsceneresize', + 'gxscenedestroy', 'gxcustomdraw', 'gxframerate', 'gxframe', 'gxscenedraw', 'gxscenemove', 'gxscenepos', 'gxscenex', + 'gxsceney', 'gxscenewidth', 'gxsceneheight', 'gxscenecolumns', 'gxscenerows', 'gxscenestart', 'gxsceneupdate', + 'gxscenefollowentity', 'gxsceneconstrain', 'gxscenestop', 'gxmapcreate', 'gxmapcolumns', 'gxmaprows', 'gxmaplayers', + 'gxmaplayervisible', 'gxmaplayeradd', 'gxmaplayerinsert', 'gxmaplayerremove', 'gxmapresize', 'gxmapdraw', + 'gxmaptileposat', 'gxmaptile', 'gxmaptile', 'gxmaptiledepth', 'gxmaptileadd', 'gxmaptileremove', 'gxmapversion', + 'gxmapsave', 'gxmapload', 'gxmapisometric', 'gxspritedraw', 'gxspritedrawscaled', 'gxtilesetcreate', + 'gxtilesetreplaceimage', 'gxtilesetload', 'gxtilesetsave', 'gxtilesetpos', 'gxtilesetwidth', 'gxtilesetheight', + 'gxtilesetcolumns', 'gxtilesetrows', 'gxtilesetfilename', 'gxtilesetimage', 'gxtilesetanimationcreate', + 'gxtilesetanimationadd', 'gxtilesetanimationremove', 'gxtilesetanimationframes', 'gxtilesetanimationspeed', + 'gxfontcreate', 'gxfontwidth', 'gxfontheight', 'gxfontcharspacing', 'gxfontlinespacing', + 'gxdrawtext', 'gxdebug', 'gxdebug', 'gxdebugscreenentities', 'gxdebugfont', 'gxdebugtilebordercolor', + 'gxdebugentitybordercolor', 'gxdebugentitycollisioncolor', 'gxkeyinput', 'gxkeydown', 'gxdeviceinputdetect', + 'gxdeviceinputtest', 'gxdevicename', 'gxdevicetypename', 'gxinputtypename', 'gxkeybuttonname']; + + var aspBuiltinObjsWords = ['server', 'response', 'request', 'session', 'application']; + var aspKnownProperties = ['buffer', 'cachecontrol', 'charset', 'contenttype', 'expires', 'expiresabsolute', 'isclientconnected', 'pics', 'status', //response + 'clientcertificate', 'cookies', 'form', 'querystring', 'servervariables', 'totalbytes', //request + 'contents', 'staticobjects', //application + 'codepage', 'lcid', 'sessionid', 'timeout', //session + 'scripttimeout']; //server + var aspKnownMethods = ['addheader', 'appendtolog', 'binarywrite', 'end', 'flush', 'redirect', //response + 'binaryread', //request + 'remove', 'removeall', 'lock', 'unlock', //application + 'abandon', //session + 'getlasterror', 'htmlencode', 'mappath', 'transfer', 'urlencode']; //server + + var knownWords = knownMethods.concat(knownProperties); + + builtinObjsWords = builtinObjsWords.concat(builtinConsts); + + if (conf.isASP){ + builtinObjsWords = builtinObjsWords.concat(aspBuiltinObjsWords); + knownWords = knownWords.concat(aspKnownMethods, aspKnownProperties); + }; + + var keywords = wordRegexp(commonkeywords); + var atoms = wordRegexp(atomWords); + var builtinFuncs = wordRegexp(builtinFuncsWords); + var builtinObjs = wordRegexp(builtinObjsWords); + var known = wordRegexp(knownWords); + var stringPrefixes = '"'; + + var opening = wordRegexp(openingKeywords); + var middle = wordRegexp(middleKeywords); + var closing = wordRegexp(endKeywords); + var doubleClosing = wordRegexp(['end']); + var doOpening = wordRegexp(['do']); + var noIndentWords = wordRegexp(['on error resume next', 'exit']); + var comment = wordRegexp(['rem']); + + + function indent(_stream, state) { + state.currentIndent++; + } + + function dedent(_stream, state) { + state.currentIndent--; + } + // tokenizers + function tokenBase(stream, state) { + if (stream.eatSpace()) { + return 'space'; + //return null; + } + + var ch = stream.peek(); + + // Handle Comments + if (ch === "'") { + stream.skipToEnd(); + return 'comment'; + } + if (stream.match(comment)){ + stream.skipToEnd(); + return 'comment'; + } + + + // Handle Number Literals + if (stream.match(/^((&H)|(&O))?[0-9\.]/i, false) && !stream.match(/^((&H)|(&O))?[0-9\.]+[a-z_]/i, false)) { + var floatLiteral = false; + // Floats + if (stream.match(/^\d*\.\d+/i)) { floatLiteral = true; } + else if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } + else if (stream.match(/^\.\d+/)) { floatLiteral = true; } + + if (floatLiteral) { + // Float literals may be "imaginary" + stream.eat(/J/i); + return 'number'; + } + // Integers + var intLiteral = false; + // Hex + if (stream.match(/^&H[0-9a-f]+/i)) { intLiteral = true; } + // Octal + else if (stream.match(/^&O[0-7]+/i)) { intLiteral = true; } + // Decimal + else if (stream.match(/^[1-9]\d*F?/)) { + // Decimal literals may be "imaginary" + stream.eat(/J/i); + // TODO - Can you have imaginary longs? + intLiteral = true; + } + // Zero by itself with no other piece of number. + else if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } + if (intLiteral) { + // Integer literals may be "long" + stream.eat(/L/i); + return 'number'; + } + } + + // Handle Strings + if (stream.match(stringPrefixes)) { + state.tokenize = tokenStringFactory(stream.current()); + return state.tokenize(stream, state); + } + + // Handle operators and Delimiters + if (stream.match(doubleOperators) + || stream.match(singleOperators) + || stream.match(wordOperators)) { + return 'operator'; + } + if (stream.match(singleDelimiters)) { + return null; + } + + if (stream.match(brakets)) { + return "bracket"; + } + + if (stream.match(noIndentWords)) { + state.doInCurrentLine = true; + + return 'keyword'; + } + + if (stream.match(doOpening)) { + indent(stream,state); + state.doInCurrentLine = true; + + return 'keyword'; + } + if (stream.match(opening)) { + if (! state.doInCurrentLine) + indent(stream,state); + else + state.doInCurrentLine = false; + + return 'keyword'; + } + if (stream.match(middle)) { + return 'keyword'; + } + + + if (stream.match(doubleClosing)) { + dedent(stream,state); + dedent(stream,state); + + return 'keyword'; + } + if (stream.match(closing)) { + if (! state.doInCurrentLine) + dedent(stream,state); + else + state.doInCurrentLine = false; + + return 'keyword'; + } + + if (stream.match(keywords)) { + return 'keyword'; + } + + if (stream.match(atoms)) { + return 'atom'; + } + + if (stream.match(known)) { + return 'variable-2'; + } + + if (stream.match(builtinFuncs)) { + return 'builtin'; + } + + if (stream.match(builtinObjs)){ + return 'variable-2'; + } + + if (stream.match(identifiers)) { + return 'variable'; + } + + // Handle non-detected items + stream.next(); + return ERRORCLASS; + } + + function tokenStringFactory(delimiter) { + var singleline = delimiter.length == 1; + var OUTCLASS = 'string'; + + return function(stream, state) { + while (!stream.eol()) { + stream.eatWhile(/[^'"]/); + if (stream.match(delimiter)) { + state.tokenize = tokenBase; + return OUTCLASS; + } else { + stream.eat(/['"]/); + } + } + if (singleline) { + if (parserConf.singleLineStringErrors) { + return ERRORCLASS; + } else { + state.tokenize = tokenBase; + } + } + return OUTCLASS; + }; + } + + + function tokenLexer(stream, state) { + var style = state.tokenize(stream, state); + var current = stream.current(); + + // Handle '.' connected identifiers + if (current === '.') { + style = state.tokenize(stream, state); + + current = stream.current(); + if (style && (style.substr(0, 8) === 'variable' || style==='builtin' || style==='keyword')){//|| knownWords.indexOf(current.substring(1)) > -1) { + if (style === 'builtin' || style === 'keyword') style='variable'; + if (knownWords.indexOf(current.substr(1)) > -1) style='variable-2'; + + return style; + } else { + return ERRORCLASS; + } + } + + return style; + } + + var external = { + electricChars:"dDpPtTfFeE ", + startState: function() { + return { + tokenize: tokenBase, + lastToken: null, + currentIndent: 0, + nextLineIndent: 0, + doInCurrentLine: false, + ignoreKeyword: false + + + }; + }, + + token: function(stream, state) { + if (stream.sol()) { + state.currentIndent += state.nextLineIndent; + state.nextLineIndent = 0; + state.doInCurrentLine = 0; + } + var style = tokenLexer(stream, state); + + state.lastToken = {style:style, content: stream.current()}; + + if (style==='space') style=null; + + return style; + }, + + indent: function(state, textAfter) { + var trueText = textAfter.replace(/^\s+|\s+$/g, '') ; + if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1); + if(state.currentIndent < 0) return 0; + return state.currentIndent * conf.indentUnit; + } + + }; + return external; +}); + +CodeMirror.defineMIME("text/vbscript", "vbscript"); + +}); diff --git a/gx/__gx_font_default.png b/gx/__gx_font_default.png new file mode 100644 index 0000000000000000000000000000000000000000..c2b694d2acc50b1473686d4a759401adfca1b127 GIT binary patch literal 7454 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 literal 0 HcmV?d00001 diff --git a/gx/__gx_font_default_black.png b/gx/__gx_font_default_black.png new file mode 100644 index 0000000000000000000000000000000000000000..ddae897199c17e5601c73c5e2e4f064692d8190e GIT binary patch literal 6667 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 Vxu6i^TCXWzj;D{uu|*LZ{|o!_0KNbK literal 0 HcmV?d00001 diff --git a/gx/gx.js b/gx/gx.js new file mode 100644 index 0000000..1da0644 --- /dev/null +++ b/gx/gx.js @@ -0,0 +1,2567 @@ +var GX = new function() { + var _canvas = null; + var _ctx = null; + var _framerate = 60; + var _bg = []; + var _images = []; + var _entities = []; + var _scene = {}; + var _tileset = {}; + var _map = {}; + var _map_layers = []; + var _map_layer_info = []; + var _map_loading = false; + var _gravity = 9.8 * 8; + var _terminal_velocity = 300; + var _fonts = new Array(2); + _fonts[0] = { eid:0, charSpacing:0, lineSpacing: 0}; + _fonts[1] = { eid:0, charSpacing:0, lineSpacing: 0}; + var _font_charmap = new Array(2).fill(new Array(256).fill({x:0,y:0})); + var _fullscreenFlag = false; + var __debug = { + enabled: false, + font: 1 // GX.FONT_DEFAULT + }; + var _sounds = []; + var _sound_muted = false; + var _mouseButtons = [0,0,0]; + var _mousePos = { x:0, y:0 }; + var _mouseInputFlag = false; + + + // javascript specific + var _onGameEvent = null; + var _pressedKeys = {}; + + async function _registerGameEvents(fnEventCallback) { + _onGameEvent = fnEventCallback; + + // wait for all of the resources to load + while (!GX.resourcesLoaded()) { + await _sleep(100); + } + } + + function __newFont() { + return { eid:0, charSpacing:0, lineSpacing: 0} + } + + function _reset() { + // TODO: stop any sounds that are currently playing + _framerate = 60; + _bg = []; + _images = []; + _entities = []; + _scene = {}; + _tileset = {}; + _map = {}; + _map_layers = []; + _map_layer_info = []; + _map_loading = false; + _gravity = 9.8 * 8; + _terminal_velocity = 300; + + _fonts = new Array(2); + _fonts[0] = { eid:0, charSpacing:0, lineSpacing: 0}; + _fonts[1] = { eid:0, charSpacing:0, lineSpacing: 0}; + _font_charmap = new Array(2).fill(new Array(256).fill({x:0,y:0})); + _fontCreateDefault(GX.FONT_DEFAULT); + _fontCreateDefault(GX.FONT_DEFAULT_BLACK); + + _fullscreenFlag = false; + __debug = { + enabled: false, + font: 1 // GX.FONT_DEFAULT + }; + _sounds = []; + _sound_muted = false; + _mouseButtons = [0,0,0]; + _mousePos = { x:0, y:0 }; + _mouseInputFlag = false; + + // javascript specific + _onGameEvent = null; + _pressedKeys = {}; + } + + // Scene Functions + // ----------------------------------------------------------------- + function _sceneCreate(width, height) { + _canvas = document.getElementById("gx-canvas"); + if (!_canvas) { + _canvas = document.createElement("canvas"); + _canvas.id = "gx-canvas"; + document.getElementById("gx-container").appendChild(_canvas); + + _canvas.addEventListener("mousemove", function(event) { + _mousePos.x = event.offsetX; + _mousePos.y = event.offsetY; + _mouseInputFlag = true; + }); + + _canvas.addEventListener("mousedown", function(event) { + event.preventDefault(); + if (event.button == 0) { + _mouseButtons[0] = -1; + } + else if (event.button == 1) { + _mouseButtons[2] = -1; + } + else if (event.button == 2) { + _mouseButtons[1] = -1; + } + _mouseInputFlag = true; + }); + + _canvas.addEventListener("mouseup", function(event) { + if (event.button == 0) { + _mouseButtons[0] = 0; + } + else if (event.button == 1) { + _mouseButtons[2] = 0; + } + else if (event.button == 2) { + _mouseButtons[1] = 0; + } + _mouseInputFlag = true; + }); + + _canvas.addEventListener("contextmenu", function(event) { + event.preventDefault(); + }); +/* + _canvas.addEventListener("keyup", function(event) { + if (_scene.active) { + event.preventDefault(); + } + _pressedKeys[event.keyCode] = false; + }); + _canvas.addEventListener("keydown", function(event) { + if (_scene.active) { + event.preventDefault(); + } + _pressedKeys[event.keyCode] = true; + }); + */ + } + _canvas.width = width; + _canvas.height = height; + _ctx = _canvas.getContext("2d"); + + var footer = document.getElementById("gx-footer"); + footer.style.width = width; + + _scene.width = width; + _scene.height = height; + _scene.x = 0; + _scene.y = 0; + _scene.frame = 0; + _scene.followMode = GX.SCENE_FOLLOW_NONE; + _scene.followEntity = null; + _scene.constrainMode = GX.SCENE_CONSTRAIN_NONE; + _scene.active = false; + + _customEvent(GX.EVENT_INIT); + } + + // Scale the scene by the specified scale factor. + function _sceneScale (scale) { + _scene.scaleX = scale; + _scene.scaleY = scale; + _canvas.width = _scene.width * _scene.scaleX; + _canvas.height = _scene.height * _scene.scaleY; + _ctx.scale(_scene.scaleX, _scene.scaleY); + _ctx.imageSmoothingEnabled = false; + + var footer = document.getElementById("gx-footer"); + footer.style.width = _canvas.width; + } + + function _sceneX() { return _scene.x; } + function _sceneY() { return _scene.y; } + function _sceneWidth() { return _scene.width; } + function _sceneHeight() { return _scene.height; } + function _sceneColumns() { return _scene.columns; } + function _sceneRows() { return _scene.rows; } + + + // Draw the scene. + // This method is called automatically when GX is managing the event/game loop. + // Call this method for each page draw event when the event/game loop is being + // handled externally. + function _sceneDraw() { + if (_map_loading) { return; } + var ei, ei2, frame; + frame = _scene.frame % GX.frameRate() + 1; + + // If the screen has been resized, resize the destination screen image + //If _Resize And Not GXSceneEmbedded Then + // '_FREEIMAGE _SOURCE + // 'SCREEN _NEWIMAGE(_RESIZEWIDTH, _RESIZEHEIGHT, 32) + // GXSceneWindowSize _ResizeWidth, _ResizeHeight + //End If + + // Clear the background + _ctx.clearRect(0, 0, GX.sceneWidth(), GX.sceneHeight()); + + // Draw background images, if present + for (var bi = 1; bi <= _bg.length; bi++) { + _backgroundDraw(bi); + } + + // Call out to any custom screen drawing + _customDrawEvent(GX.EVENT_DRAWBG); + + // Draw the map tiles + GX.mapDraw(); + + // Call out to any custom screen drawing + _customDrawEvent(GX.EVENT_DRAWMAP); + + // Draw the entities + for (var ei = 1; ei <= _entities.length; ei++) { + var e = _entities[ei-1]; + if (!e.screen) { + if (_rectCollide(e.x, e.y, e.width, e.height, GX.sceneX(), GX.sceneY(), GX.sceneWidth(), GX.sceneHeight())) { + _entityDraw(e); + } + } + if (e.animate > 0) { + if (frame % (GX.frameRate() / e.animate) == 0) { + GX.entityFrameNext(ei); + } + } + } + + // Draw the screen entities which should appear on top of the other game entities + // and have a fixed position + for (var ei = 1; ei <= _entities.length; ei++) { + var e = _entities[ei-1]; + if (e.screen) { + _entityDraw(e); + } + } + + // Call out to any custom screen drawing + _customDrawEvent(GX.EVENT_DRAWSCREEN); + if (GX.debug()) { _debugFrameRate(); } + + // Copy the background image to the screen + _customEvent(GX.EVENT_PAINTBEFORE); + //_DontBlend + //_PutImage , __gx_scene.image + //_Blend + _customEvent(GX.EVENT_PAINTAFTER); + } + + function _sceneUpdate() { + _scene.frame++; + if (_map_loading) { return; } + + // Call custom game update logic + _customEvent(GX.EVENT_UPDATE); + + // Perform any movement for registered player entities + //Dim i As Integer + //For i = 1 To __gx_player_count + // __GX_PlayerAction i + //Next i + + // Check for entity movement and collisions + // TODO: filter out non-moving entities + _sceneMoveEntities(); + + // Perform any auto-scene moves + var sx, sy; + if (_scene.followMode == GX.SCENE_FOLLOW_ENTITY_CENTER || + _scene.followMode == GX.SCENE_FOLLOW_ENTITY_CENTER_X || + _scene.followMode == GX.SCENE_FOLLOW_ENTITY_CENTER_X_POS || + _scene.followMode == GX.SCENE_FOLLOW_ENTITY_CENTER_X_NEG) { + sx = (GX.entityX(_scene.followEntity) + GX.entityWidth(_scene.followEntity) / 2) - GX.sceneWidth() / 2; + if (sx < GX.sceneX() && _scene.followMode == GX.SCENE_FOLLOW_ENTITY_CENTER_X_POS || + sx > GX.sceneX() && _scene.followMode == GX.SCENE_FOLLOW_ENTITY_CENTER_X_NEG) { + // don't move the scene + } else { + GX.scenePos(sx, GX.sceneY()); + } + } + if (_scene.followMode == GX.SCENE_FOLLOW_ENTITY_CENTER || + _scene.followMode == GX.SCENE_FOLLOW_ENTITY_CENTER_Y) { + sy = (GX.entityY(_scene.followEntity) + GX.entityHeight(_scene.followEntity) / 2) - GX.sceneHeight() / 2; + GX.scenePos(GX.sceneX(), sy); + } + + // Check the scene move constraints + if (_scene.constrainMode == GX.SCENE_CONSTRAIN_TO_MAP) { + var mwidth = GX.mapColumns() * GX.tilesetWidth(); + var mheight = GX.mapRows() * GX.tilesetHeight(); + sx = GX.sceneX(); + if (sx < 0) { + sx = 0 + } else if (sx + GX.sceneWidth() > mwidth) { + sx = mwidth - GX.sceneWidth(); + if (sx < 0) { sx = 0; } + } + + sy = GX.sceneY(); + if (sy < 0) { + sy = 0; + } else if (sy + GX.sceneHeight() > mheight) { + sy = mheight - GX.sceneHeight(); + if (sy < 0) { sy = 0; } + } + GX.scenePos(sx, sy); + } + } + + // Start the game loop. + // Game events will be sent to the GXOnGameEvent method during the game + // loop execution. + async function _sceneStart() { + + _scene.frame = 0; + _scene.active = true; + + setTimeout(_sceneLoad, 10); + + while (_scene.active) { + await _sleep(100); + } + } + + function _resourcesLoaded() { + for (var i=0; i < _images.length; i++) { + if (!_images[i].complete) { + return false; + } + } + return true; + } + + function _sceneLoad() { + if (!_resourcesLoaded()) { + setTimeout(_sceneLoad, 50); + return; + } + window.requestAnimationFrame(_sceneLoop); + } + + function _sceneLoop() { + if (!_scene.active) { return; } + + GX.sceneUpdate(); + GX.sceneDraw(); + + window.requestAnimationFrame(_sceneLoop); + } + + // Stop the game loop. + // This method will cause the game loop to end and return control to the calling program. + function _sceneStop() { + _scene.active = false; + } + + function _sceneFollowEntity (eid, mode) { + _scene.followEntity = eid; + _scene.followMode = mode; + } + + function _sceneConstrain(mode) { + _scene.constrainMode = mode; + } + + // Moves the scene position by the number of pixels specified by the dx and dy values. + // The default position for a scene is (0,0). Negative x and y values are valid. + // A non-zero value for dx will move the scene by the number of pixels specified to the right or left. + // A non-zero value for dy will move the scene by the number of pixels specified up or down. + function _sceneMove (dx, dy) { + _scene.x = GX.sceneX() + dx; + _scene.y = GX.sceneY() + dy; + } + + // Positions the scene at the specified x and y coordinates. + // The default position for a scene is (0,0). Negative x and y values are valid. + function _scenePos (x, y) { + _scene.x = x; + _scene.y = y; + } + + function _updateSceneSize() { + if (GX.tilesetWidth() < 1 || GX.tilesetHeight() < 1) { return; } + if (GX.mapIsometric()) { + _scene.columns = Math.floor(GXSceneWidth() / GXTilesetWidth()); + _scene.rows = GX.sceneHeight() / (GX.tilesetWidth() / 4); + } else { + _scene.columns = Math.floor(GX.sceneWidth() / GX.tilesetWidth()); + _scene.rows = Math.floor(GX.sceneHeight() / GX.tilesetHeight()); + } + } + + + // Event functions + // -------------------------------------------------------------------- + function _customEvent (eventType) { + var e = {}; + e.event = eventType + _onGameEvent(e); + } + + function _customDrawEvent (eventType) { + _customEvent(eventType) + } + + + function _keyDown(key) { + return _pressedKeys[key]; + } + + // Frame Functions + // ------------------------------------------------------------------- + // Gets or sets the current frame rate (expressed in frames-per-second or FPS). + function _frameRate (frameRate) { + if (frameRate != undefined) { + _framerate = frameRate; + } + return _framerate; + } + + // Returns the current frame. + // This is a frame counter that starts when GXSceneStart is called. + // It is initially set to zero and is incremented on each frame. + function _frame() { + return _scene.frame; + } + + // Image Functions + // ------------------------------------------------------------------ + function _imageLoad(filename, callbackFn) { + for (var i=0; i < _images.length; i++) { + if (filename == _images[i].src) { + return i; + } + } + var img = new Image(); + if (callbackFn != undefined) { + img.onload = function() { + callbackFn(img); + } + } + img.src = filename; + _images.push(img); + return _images.length; + } + + function _image(imgId) { + return _images[imgId-1]; + } + + function _spriteDraw(i, x, y, seq, frame, swidth, sheight) { + _spriteDrawScaled(i, x, y, swidth, sheight, seq, frame, swidth, sheight); + } + + function _spriteDrawScaled(i, x, y, dwidth, dheight, seq, frame, swidth, sheight) { + var xoffset, yoffset; + xoffset = (frame - 1) * swidth; + yoffset = (seq - 1) * sheight; + _ctx.drawImage(_image(i), xoffset, yoffset, swidth, sheight, x, y, dwidth, dheight); + } + + + // Background functions + // ---------------------------------------------------- + // Adds a new background image to the current scene. Multiple background images may be added to the scene. + // Background images are displayed in layers based on the order they are added. + // One of the following modes must be specified: + // GXBG_STRETCH - Stretch the background image to the size of the scene. + // GXBG_SCROLL - Fit the height of the background image to the size of the screen. + // Scroll the horizontal position relative to the position on the map. + // GXBG_WRAP - Continuously wrap the background as the scene is moved horizontally. + function _backgroundAdd (imageFilename, mode) { + var bg = {}; + bg.mode = mode; + bg.x = 0; + bg.y = 0; + if (mode == GX.BG_WRAP) { + _imageLoad(imageFilename, function(img) { + var imgCanvas = document.createElement("canvas"); + imgCanvas.width = img.width * 2; + imgCanvas.height = img.height; + var imgCtx = imgCanvas.getContext("2d"); + imgCtx.drawImage(img, 0, 0); + imgCtx.drawImage(img, img.width, 0); + + var dataUrl = imgCanvas.toDataURL("image/png"); + var img2 = new Image(); + img2.height = img.height; + img2.width = img.width * 2; + img2.src = dataUrl; + //document.body.append(img2); + _images.push(img2); + bg.image = _images.length; + + //_bg.push(bg); + }); + // Dim w As Integer, h As Integer + // w = _Width(img) + // h = _Height(img) + // Dim newImage As Long + // 'newImage = _NewImage(w * 2, h, 32) + // newImage = __GX_HardwareImage(w * 2, h) + // _PutImage (0, 0)-(w, h), img, newImage + // _PutImage (w, 0)-(w * 2, h), img, newImage + // _FreeImage img + // __gx_bg(__gx_bg_count).image = newImage + } else { + bg.image = _imageLoad(imageFilename); + } + + _bg.push(bg); + return _bg.length; + } + + function _backgroundDraw (bi) { + bi--; + + if (_bg[bi].mode == GX.BG_STRETCH) { + _ctx.drawImage(_image(_bg[bi].image), 0, 0, _scene.width, _scene.height); // __gx_scene.image + } + + else if (_bg[bi].mode == GX.BG_SCROLL) { + var img = _image(_bg[bi].image); + var factor = GX.sceneWidth() / GX.sceneHeight(); + var h = img.height; + var w = h * factor; + var xfactor = GX.sceneX() / (GX.mapColumns() * GX.tilesetWidth()) + var x = xfactor * (img.width - w); + _ctx.drawImage(img, x, 0, w, h, 0, 0, GX.sceneWidth(), GX.sceneHeight()); + } + + else if (_bg[bi].mode == GX.BG_WRAP) { + var img = _image(_bg[bi].image); + var h = img.height; + var w = GX.sceneWidth(); + var y = _bg[bi].y; + var x = (GX.sceneX() % img.width) / 2; + _ctx.drawImage(img, x, 0, w, img.height, 0, y, GX.sceneWidth(), h); + _bg[bi].x = x + 1; + if (_bg[bi].x > img.width / 2) { + _bg[bi].x = 0; + } + } + } + + function _backgroundY (bi, y) { + if (y != undefined) { + _bg[bi-1].y = y; + } + return _bg[bi-1].y; + } + + function _backgroundHeight (bi, height) { + if (height != undefined) { + _bg[bi-1].height = height; + } + return _bg[bi-1].height; + } + + // Removes all background images from the scene. + function _backgroundClear() { + _bg.length = 0; + } + + + // Sound Methods + // ---------------------------------------------------------------------------- + function _soundLoad (filename) { + var a = new Audio(filename); + _sounds.push(a); + return _sounds.length; + } + + function _soundPlay (sid) { + if (!GX.soundMuted()) { + _sounds[sid-1].loop = false; + _sounds[sid-1].play(); + } + } + + function _soundRepeat (sid) { + if (!GX.soundMuted()) { + _sounds[sid-1].loop = true; + _sounds[sid-1].play(); + } + } + + function _soundVolume (sid, v) { + _sounds[sid-1].volume = v / 100; + } + + function _soundPause (sid) { + _sounds[sid-1].pause(); + } + + function _soundStop (sid) { + _sounds[sid-1].pause(); + // TODO: reset playback position to beginning + } + + function _soundMuted (muted) { + if (muted != undefined) { + _sound_muted = muted; + // TODO: loop through list of loaded sounds so they can all be muted / unmuted + } + return _sound_muted; + } + + + // Entity Functions + // ----------------------------------------------------------------------------- + function _entityCreate (imageFilename, ewidth, height, seqFrames, uid) { + var newent = {}; + newent.x = 0; + newent.y = 0; + newent.vx = 0; + newent.vy = 0; + newent.jumpstart = 0; + newent.height = height; + newent.width = ewidth; + newent.image = _imageLoad(imageFilename); + newent.spriteFrame = 1; + newent.spriteSeq = 1; + newent.seqFrames = seqFrames; + newent.hidden = false; + newent.animateMode = GX.ANIMATE_LOOP; + newent.coLeft = 0; + newent.coTop = 0; + newent.coRight = 0; + newent.coBottom = 0; + newent.applyGravity = false; + _entities.push(newent); + + // TODO: reincorporate uid + + return _entities.length; + } + + function _screenEntityCreate (imageFilename, ewidth, height, seqFrames, uid) { + var eid = _entityCreate(imageFilename, ewidth, height, seqFrames, uid); + _entities[eid-1].screen = true; + return eid; + } + + function _entityDraw (ent) { + if (ent.hidden) { return; } + var x, y; + if (ent.screen == 1) { + x = ent.x + y = ent.y + } else { + x = ent.x - GX.sceneX() + y = ent.y - GX.sceneY() + } + GX.spriteDraw(ent.image, x, y, ent.spriteSeq, ent.spriteFrame, ent.width, ent.height); //, __gx_scene.image) + } + + function _entityAnimate (eid, seq, a) { + _entities[eid-1].animate = a; + _entities[eid-1].spriteSeq = seq; + _entities[eid-1].prevFrame = -1; + } + + function _entityAnimateStop (eid) { + _entities[eid-1].animate = 0; + } + + function _entityAnimateMode (eid, mode) { + if (mode != undefined) { + _entities[eid-1].animateMode = mode; + } + return _entities[eid-1].animateMode; + } + + function _entityMove (eid, x, y) { + if (eid == undefined || eid < 1) { return; } + _entities[eid-1].x += x; + _entities[eid-1].y += y; + } + + function _entityPos (eid, x, y) { + _entities[eid-1].x = x; + _entities[eid-1].y = y; + } + + function _entityVX (eid, vx) { + if (vx != undefined) { + _entities[eid-1].vx = vx; + } + return _entities[eid-1].vx; + } + + function _entityVY (eid, vy) { + if (vy != undefined) { + _entities[eid-1].vy = vy; + } + return _entities[eid-1].vy; + } + + function _entityVisible (eid, visible) { + if (visible != undefined) { + _entities[eid-1].hidden = !visible; + } + return !_entities[eid-1].hidden; + } + + function _entityX (eid) { return _entities[eid-1].x; } + function _entityY (eid) { return _entities[eid-1].y; } + function _entityWidth (eid) { return _entities[eid-1].width; } + function _entityHeight (eid) { return _entities[eid-1].height; } + + + function _entityFrameNext (eid) { + if (_entities[eid-1].animateMode == GX.ANIMATE_SINGLE) { + if (_entities[eid-1].spriteFrame + 1 >= _entities[eid-1].seqFrames) { + if (_entities[eid-1].spriteFrame != _entities[eid-1].prevFrame) { + // Fire animation complete event + var e = {}; + e.event = GX.EVENT_ANIMATE_COMPLETE; + e.entity = eid; + _onGameEvent(e); + _entities[eid-1].prevFrame = _entities[eid-1].spriteFrame; + } + return; + } + } + + _entities[eid-1].prevFrame = _entities[eid-1].spriteFrame; + _entities[eid-1].spriteFrame = _entities[eid-1].spriteFrame + 1; + if (_entities[eid-1].spriteFrame > _entities[eid-1].seqFrames) { + _entities[eid-1].spriteFrame = 1; + } + } + + function _entityFrameSet (eid, seq, frame) { + _entities[eid-1].spriteSeq = seq; + _entities[eid-1].spriteFrame = frame; + _entities[eid-1].prevFrame = frame - 1; + } + + function _entityType (eid, etype) { + if (etype != undefined) { + _entities[eid-1].type = etype; + } + return _entities[eid-1].type + } + + function _entityApplyGravity (eid, gravity) { + if (gravity != undefined) { + _entities[eid-1].applyGravity = gravity; + _entities[eid-1].jumpstart = GX.frame(); + } + return _entities[eid-1].applyGravity; + } + + function _entityCollisionOffset (eid, left, top, right, bottom) { + _entities[eid-1].coLeft = left; + _entities[eid-1].coTop = top; + _entities[eid-1].coRight = right; + _entities[eid-1].coBottom = bottom; + } + + function _entityCollisionOffsetLeft (eid) { + return _entities[eid-1].coLeft; + } + + function _entityCollisionOffsetTop (eid) { + return _entities[eid-1].coTop; + } + + function _entityCollisionOffsetRight (eid) { + return _entities[eid-1].coRight; + } + + function _entityCollisionOffsetBottom (eid) { + return _entities[eid-1].coBottom; + } + + + // Map methods + // ------------------------------------------------------------------ + function _mapCreate (columns, rows, layers) { + _map.columns = columns; + _map.rows = rows; + _map.layers = layers; + _map.version = 2; + + var layerSize = rows * columns; + _map_layers = []; + for (var i=0; i < layers; i++) { + _map_layers.push(_mapLayerInit()); + } + _map_layer_info = []; + for (var i=0; i < layers; i++) { + _map_layer_info.push({ + id: i+1, + hidden: false + }); + } + } + + async function _mapLoad(filename) { + _map_loading = true; + var data = await _getJSON(filename); + var parentPath = filename.substring(0, filename.lastIndexOf("/")+1); + var imagePath = data.tileset.image.substring(data.tileset.image.lastIndexOf("/")+1); + GX.tilesetCreate(parentPath + imagePath, data.tileset.width, data.tileset.height); + GX.mapCreate(data.columns, data.rows, data.layers.length); + for (var layer=0; layer < data.layers.length; layer++) { + for (var row=0; row < GX.mapRows(); row++) { + for (var col=0; col < GX.mapColumns(); col++) { + GX.mapTile(col, row, layer+1, data.layers[layer][row * GX.mapColumns() + col]); + } + } + } + _map_loading = false; + } + + function _getJSON(url) { + return fetch(url) + .then((response)=>response.json()) + .then((responseJson)=>{return responseJson}); + } + + function _mapLayerInit() { + var layerSize = _map.rows * _map.columns; + var layerData = []; + for (var i=0; i < layerSize; i++) { + layerData.push({ tile: 0}); + } + return layerData; + } + + function _mapColumns() { return _map.columns; } + function _mapRows() { return _map.rows; } + function _mapLayers() { return _map.layers; } + + function _mapLayerVisible(layer, visible) { + if (visible != undefined) { + _map_layer_info[layer-1].hidden = !visible; + } + return !_map_layer_info[layer-1].hidden; + } + + function _mapIsometric(iso) { + if (iso != undefined) { + _map.isometric = iso; + _updateSceneSize(); + } + return _map.isometric; + } + + function _mapLayerAdd() { + _map.layers++; + _map_layer_info.push({ + id: _map.layers, + hidden: false + }); + _map_layers.push(_mapLayerInit()); + } +/* + Sub GXMapLayerInsert (beforeLayer As Integer) + If beforeLayer < 1 Or beforeLayer > GXMapLayers Then Exit Sub + + GXMapLayerAdd + Dim layer As Integer + Dim tile As Integer + For layer = GXMapLayers To beforeLayer + 1 Step -1 + 'gx_map_layer_info(layer) = gx_map_layer_info(layer - 1) + For tile = 0 To GXMapRows * GXMapColumns + __gx_map_layers(tile, layer) = __gx_map_layers(tile, layer - 1) + Next tile + Next layer + Dim blankTile As GXMapTile + For tile = 0 To GXMapRows * GXMapColumns + __gx_map_layers(tile, beforeLayer) = blankTile + Next tile + End Sub + + Sub GXMapLayerRemove (removeLayer As Integer) + If removeLayer < 1 Or removeLayer > GXMapLayers Or GXMapLayers < 2 Then Exit Sub + + Dim layer As Integer + Dim tile As Integer + For layer = removeLayer To GXMapLayers - 1 + For tile = 0 To GXMapRows * GXMapColumns + __gx_map_layers(tile, layer) = __gx_map_layers(tile, layer + 1) + Next tile + Next layer + + ReDim _Preserve __gx_map_layer_info(GXMapLayers - 1) As GXMapLayer + ReDim _Preserve __gx_map_layers(GXMapRows * GXMapColumns, GXMapLayers - 1) As GXMapTile + __gx_map.layers = GXMapLayers - 1 + End Sub + + Sub GXMapResize (columns As Integer, rows As Integer) + Dim tempMap(GXMapRows * GXMapColumns, GXMapLayers) As GXMapTile + Dim m1 As _MEM: m1 = _Mem(__gx_map_layers()) + Dim m2 As _MEM: m2 = _Mem(tempMap()) + _MemCopy m1, m1.OFFSET, m1.SIZE To m2, m2.OFFSET + _MemFree m1 + _MemFree m2 + + ReDim __gx_map_layers(rows * columns, GXMapLayers) As GXMapTile + + Dim layer As Integer + Dim row As Integer + Dim column As Integer + Dim maxColumns As Integer + Dim maxRows As Integer + If columns > GXMapColumns Then + maxColumns = GXMapColumns + Else + maxColumns = columns + End If + If rows > GXMapRows Then + maxRows = GXMapRows + Else + maxRows = rows + End If + + Dim blankTile As GXMapTile + For layer = 1 To GXMapLayers + For row = 0 To maxRows - 1 + For column = 0 To maxColumns - 1 + If column >= GXMapColumns Or row >= GXMapRows Then + __gx_map_layers(row * columns + column, layer) = blankTile + Else + __gx_map_layers(row * columns + column, layer) = tempMap(row * GXMapColumns + column, layer) + End If + Next column + Next row + Next layer + + __gx_map.columns = columns + __gx_map.rows = rows + End Sub +*/ + function _mapDraw() { + if (_mapRows() < 1) { return; } + + var tpos = {}; + var srow, scol, row, col; + var layer; + var yoffset, prow; + var t, tx, ty; + var rowOffset; + var colOffset; + + var xoffset = GX.sceneX() % GX.tilesetWidth(); + var pcol = Math.floor(GX.sceneX() / GX.tilesetWidth()); + if (GX.mapIsometric()) { + prow = Math.floor(GX.sceneY() / (GX.tilesetWidth() / 4)); + yoffset = GX.sceneY() % (GX.tilesetWidth() / 4); + } else { + prow = Math.floor(GX.sceneY() / GX.tilesetHeight()); + yoffset = GX.sceneY() % GX.tilesetHeight(); + } + + for (var li = 1; li <= GX.mapLayers(); li++) { + if (!_map_layer_info[li-1].hidden) { + layer = _map_layer_info[li-1].id; + + srow = 0; + rowOffset = 0; + + for (row = prow; row <= prow + GX.sceneRows() + 1; row++) { //TODO: currently rendering too many rows for isometric maps + scol = 0; + if (!GX.mapIsometric()) { + colOffset = 0; + } else { + colOffset = 0; + if (row % 2 == 0) { colOffset = GXTilesetWidth / 2; } + } + + if (GX.mapIsometric()) { + rowOffset = (row - prow + 1) * (GX.tilesetHeight() - GX.tilesetWidth() / 4); + } + + for (col = pcol; col <= pcol + GX.sceneColumns() + 1; col++) { + t = GX.mapTile(col, row, layer); + if (t > 0) { + var t1 = t; + t = _tileFrame(t); + GX.tilesetPos(t, tpos); + tx = scol * GX.tilesetWidth() - xoffset - colOffset; + ty = srow * GX.tilesetHeight() - yoffset - rowOffset; + GX.spriteDraw(GX.tilesetImage(), tx, ty, tpos.y, tpos.x, GX.tilesetWidth(), GX.tilesetHeight());//, __gx_scene.image + } + scol = scol + 1; + } + srow = srow + 1; + } + } // layer is not hidden + } + + // Perform tile animation + for (t = 1; t <= GX.tilesetColumns() * GX.tilesetRows(); t++) { + _tileFrameNext(t); + } + } + + function _mapTilePosAt (x, y, tpos) { + if (!GX.mapIsometric()) { + tpos.x = Math.floor((x + GX.sceneX()) / GX.tilesetWidth()); + tpos.y = Math.floor((y + GX.sceneY()) / GX.tilesetHeight()); + } else { + var tileWidthHalf = GX.tilesetWidth() / 2; + var tileHeightHalf = GX.tilesetHeight() / 2; + var sx = x / tileWidthHalf; + + var offset = 0; + if (sx % 2 == 1) { + offset = tileWidthHalf; + } + + tpos.y = (2 * y) / tileHeightHalf; + tpos.x = (x - offset) / GX.tilesetWidth(); + } + } + + function _mapTile (col, row, layer, tile) { + if (col < 0 || col >= GX.mapColumns() || row < 0 || row >= GX.mapRows() || layer > GX.mapLayers()) { return 0; } + var mpos = row * GX.mapColumns() + col; + if (tile >= 0) { + if (col >= 0 && col <= GX.mapColumns() && row >= 0 && row < GX.mapRows()) { + _map_layers[layer-1][mpos].tile = tile; + } + } + return _map_layers[layer-1][mpos].tile; + } +/* + + Function GXMapTileDepth (col As Integer, row As Integer) + If col < 0 Or col >= GXMapColumns Or row < 0 Or row >= GXMapRows Then + GXMapTileDepth = 0 + Else + Dim layer As Integer + For layer = GXMapLayers To 1 Step -1 + If GXMapTile(col, row, layer) > 0 Then + GXMapTileDepth = layer + Exit Function + End If + Next layer + GXMapTileDepth = 0 + End If + End Function + + Sub GXMapTileAdd (col As Integer, row As Integer, tile As Integer) + If tile < 1 Then Exit Sub + 'TODO: check for tile greater than max and exit early + + If (col >= 0 And col <= GXMapColumns And row >= 0 And row <= GXMapRows) Then + Dim layer As Integer + For layer = 1 To GXMapLayers + If GXMapTile(col, row, layer) = 0 Then + GXMapTile col, row, layer, tile + Exit Sub + End If + Next layer + End If + End Sub + + Sub GXMapTileRemove (col As Integer, row As Integer) + If (col >= 0 And col <= GXMapColumns And row >= 0 And row < GXMapRows) Then + Dim layer As Integer + For layer = GXMapLayers To 1 Step -1 + If GXMapTile(col, row, layer) Then + GXMapTile col, row, layer, 0 + Exit Sub + End If + Next layer + End If + End Sub + + Function GXMapVersion + GXMapVersion = __gx_map.version + End Function + */ + + + // Tileset Methods + // ---------------------------------------------------------------------------- + function _tilesetCreate (tilesetFilename, tileWidth, tileHeight) { + GX.tilesetReplaceImage(tilesetFilename, tileWidth, tileHeight); + + _tileset_tiles = []; + for (var i=0; i < GX.tilesetColumns() * GX.tilesetRows(); i++) { + _tileset_tiles.push({ + id: i+1, + animationId: 0, + animationSpeed: 0, + animationFrame: 0 + }); + } + } + + function _tilesetReplaceImage (tilesetFilename, tilewidth, tileheight) { + _tileset.filename = tilesetFilename; + _tileset.width = tilewidth; + _tileset.height = tileheight; + _tileset.image = _imageLoad(tilesetFilename, function(img) { + _tileset.columns = img.width / GX.tilesetWidth(); + _tileset.rows = img.height / GX.tilesetHeight(); + _updateSceneSize(); + }); + } +/* + Sub GXTilesetLoad (filename As String) + Open filename For Binary As #1 + __GX_TilesetLoad + Close #1 + End Sub + + Sub __GX_TilesetLoad + Dim version As Integer + version = 1 + + ' Save the tileset version + Get #1, , version + + ' Save the tileset image meta-data + __gx_tileset.filename = __GX_ReadString + Get #1, , __gx_tileset.width + Get #1, , __gx_tileset.height + + ' Read the tileset image data and save to a temporary location + Dim tsize As Long + Get #1, , tsize + Dim tmpfile As String + Dim tilesetFilename As String + Dim bytes(tsize) As _Unsigned _Byte + tmpfile = GXFS_RemoveFileExtension(GXFS_GetFilename(GXTilesetFilename)) + ".gxi" + If Not _DirExists("./tmp") Then MkDir ("./tmp") + tilesetFilename = "./tmp/" + tmpfile + Get #1, , bytes() + Open tilesetFilename For Binary As #2 + Put #2, , bytes() + Close #2 + GXTilesetCreate tilesetFilename, GXTilesetWidth, GXTilesetHeight + + ' Read the tileset tile data + Dim asize As Integer + Get #1, , asize + ReDim __gx_tileset_tiles(asize) As GXTile + Get #1, , __gx_tileset_tiles() + + ' Read the tileset animation data + Get #1, , asize + ReDim __gx_tileset_animations(asize) As GXTileFrame + Get #1, , __gx_tileset_animations() + End Sub + + Sub GXTilesetSave (filename As String) + Open filename For Binary As #1 + __GX_TilesetSave + Close #1 + End Sub + + Sub __GX_TilesetSave + Dim version As Integer + version = 1 + + ' Save the tileset version + Put #1, , version + + ' Save the tileset image meta-data + __GX_WriteString GXTilesetFilename + Put #1, , __gx_tileset.width + Put #1, , __gx_tileset.height + + ' Save the tileset image data + Dim tsize As Long + Open GXTilesetFilename For Binary As #2 + tsize = LOF(2) + Put #1, , tsize + + Dim bytes(tsize) As _Unsigned _Byte + Get #2, , bytes() + Put #1, , bytes() + Close #2 + + ' Save the tileset tile data + Dim asize As Integer + asize = UBound(__gx_tileset_tiles) + Put #1, , asize + Put #1, , __gx_tileset_tiles() + + ' Save the tileset animation data + asize = UBound(__gx_tileset_animations) + Put #1, , asize + Put #1, , __gx_tileset_animations() + End Sub +*/ + function _tilesetPos (tilenum, p) { + if (GX.tilesetColumns() == 0) { + p.x = 0; + p.y = 0; + } else { + p.y = Math.floor((tilenum - 1) / GX.tilesetColumns()); + p.y = p.y + 1; + p.x = (tilenum - 1) % GX.tilesetColumns() + 1; + } + } + + function _tilesetWidth() { return _tileset.width; } + function _tilesetHeight() { return _tileset.height; } + function _tilesetColumns() { return _tileset.columns; } + function _tilesetRows() { return _tileset.rows; } + function _tilesetFilename() { return _tileset.filename; } + function _tilesetImage() { return _tileset.image; } + +/* + Sub GXTilesetAnimationCreate (tileId As Integer, animationSpeed As Integer) + Dim frameId As Integer + frameId = UBound(__gx_tileset_animations) + 1 + ReDim _Preserve __gx_tileset_animations(frameId) As GXTileFrame + __gx_tileset_animations(frameId).tileId = tileId + __gx_tileset_animations(frameId).firstFrame = frameId + __gx_tileset_tiles(tileId).animationId = frameId + __gx_tileset_tiles(tileId).animationSpeed = animationSpeed + End Sub + + Sub GXTilesetAnimationAdd (firstTileId As Integer, addTileId As Integer) + Dim firstFrame As Integer + firstFrame = __gx_tileset_tiles(firstTileId).animationId + + ' find the last frame + Dim lastFrame As Integer + lastFrame = firstFrame + While __gx_tileset_animations(lastFrame).nextFrame > 0 + lastFrame = __gx_tileset_animations(lastFrame).nextFrame + Wend + + Dim frameId As Integer + frameId = UBound(__gx_tileset_animations) + 1 + ReDim _Preserve __gx_tileset_animations(frameId) As GXTileFrame + __gx_tileset_animations(frameId).tileId = addTileId + __gx_tileset_animations(frameId).firstFrame = firstFrame + __gx_tileset_animations(lastFrame).nextFrame = frameId + End Sub + + Sub GXTilesetAnimationRemove (firstTileId As Integer) + ' TODO: replace with implementation that will remove unused + ' animation data from the array + __gx_tileset_tiles(firstTileId).animationId = 0 + End Sub + + Function GXTilesetAnimationFrames (tileId As Integer, tileFrames() As Integer) + If tileId < 0 Or tileId > GXTilesetRows * GXTilesetColumns Then + GXTilesetAnimationFrames = 0 + Exit Function + End If + + ReDim tileFrames(0) As Integer + Dim frame As Integer + Dim frameCount As Integer + frameCount = 0 + frame = __gx_tileset_tiles(tileId).animationId + While frame > 0 + frameCount = frameCount + 1 + ReDim _Preserve tileFrames(frameCount) As Integer + tileFrames(frameCount) = __gx_tileset_animations(frame).tileId + frame = __gx_tileset_animations(frame).nextFrame + Wend + GXTilesetAnimationFrames = frameCount + End Function + + Function GXTilesetAnimationSpeed (tileId As Integer) + If tileId > GXTilesetRows * GXTilesetColumns Then Exit Function + GXTilesetAnimationSpeed = __gx_tileset_tiles(tileId).animationSpeed + End Function + + Sub GXTilesetAnimationSpeed (tileId As Integer, speed As Integer) + 'IF tileId < 1 THEN EXIT SUB + + 'DIM firstFrame AS INTEGER + 'firstFrame = gx_tileset_tiles(tileId).animationId + 'IF firstFrame < 1 THEN EXIT SUB + + 'gx_tileset_animations(firstFrame).animationSpeed = speed + __gx_tileset_tiles(tileId).animationSpeed = speed + End Sub +*/ + function _tileFrame (tileId) { + return tileId; +/* if (tileId < 0 || tileId > _tileset_tiles.length) { + return tileId; + } + + if _tileset_tiles(tileId).animationId = 0 Then + __GX_TileFrame = tileId + Exit Function + End If + + Dim currFrame As Integer + currFrame = __gx_tileset_tiles(tileId).animationId + If __gx_tileset_tiles(tileId).animationFrame > 0 Then + currFrame = __gx_tileset_tiles(tileId).animationFrame + End If + + __GX_TileFrame = __gx_tileset_animations(currFrame).tileId +*/ + } + + + function _tileFrameNext (tileId) { + /* + If tileId < 0 Or tileId > UBound(__gx_tileset_tiles) Then Exit Sub + If __gx_tileset_tiles(tileId).animationId = 0 Then Exit Sub + + Dim frame As Integer + frame = GXFrame Mod GXFrameRate + 1 + + Dim firstFrame As Integer + firstFrame = __gx_tileset_tiles(tileId).animationId + + Dim animationSpeed As Integer + animationSpeed = __gx_tileset_tiles(tileId).animationSpeed + + If frame Mod (GXFrameRate / animationSpeed) = 0 Then + Dim currFrame As Integer + currFrame = firstFrame + If __gx_tileset_tiles(tileId).animationFrame > 0 Then + currFrame = __gx_tileset_tiles(tileId).animationFrame + End If + + Dim nextFrame As Integer + nextFrame = __gx_tileset_animations(currFrame).nextFrame + If nextFrame = 0 Then + nextFrame = firstFrame + End If + + __gx_tileset_tiles(tileId).animationFrame = nextFrame + End If + */ + } + + + // Miscellaneous Private Methods + // --------------------------------------------------------------------------- + function _entityCollide (eid1, eid2) { + return _rectCollide( + GX.entityX(eid1), GX.entityY(eid1), GX.entityWidth(eid1), GX.entityHeight(eid1), + GX.entityX(eid2), GX.entityY(eid2), GX.entityWidth(eid2), GX.entityHeight(eid2)); + } + + function _rectCollide(r1x1, r1y1, r1w, r1h, r2x1, r2y1, r2w, r2h) { + var r1x2 = r1x1 + r1w; + var r1y2 = r1y1 + r1h; + var r2x2 = r2x1 + r2w; + var r2y2 = r2y1 + r2h; + + var collide = 0; + if (r1x2 >= r2x1) { + if (r1x1 <= r2x2) { + if (r1y2 >= r2y1) { + if (r1y1 <= r2y2) { + collide = 1; + } + } + } + } + return collide; + } + + function _sceneMoveEntities() { + var frameFactor = 1 / GX.frameRate(); + + for (var eid = 1; eid <= _entities.length; eid++) { + if (!_entities[eid-1].screen) { + //alert(eid + ":" + GX.entityVX(eid)); + _sceneMoveEntity(eid); + + // apply the move vector to the entity's position + if (GX.entityVX(eid)) { + _entities[eid-1].x = GX.entityX(eid) + GX.entityVX(eid) * frameFactor; + } + if (GX.entityVY(eid)) { + _entities[eid-1].y = GX.entityY(eid) + GX.entityVY(eid) * frameFactor; + } + } + } + } + + function _sceneMoveEntity(eid) { + var tpos = {}; + var centity = 0; // INTEGER + var tmove = 0; // INTEGER + var testx = 0; // INTEGER + var testy = 0 ; // INTEGER +//alert(GX.entityVX(eid)); + + // Test upward movement + if (GX.entityVY(eid) < 0) { + testy = Math.round(GX.entityVY(eid) / GX.frameRate()); + if (testy > -1) { testy = -1; } + tmove = Math.round(_entityTestMove(eid, 0, testy, tpos, centity)); + if (tmove == 0) { + if (GX.entityApplyGravity(eid)) { + // reverse the motion + GX.entityVY(eid, GX.entityVY(eid) * -.5); + } else { + // stop the motion + GX.entityVY(eid, 0); + } + + // don't let the entity pass into the collision entity or tile + if (centity > 0) { + GX.entityPos(eid, GX.entityX(eid), GX.entityY(centity) - GX.entityCollisionOffsetBottom(centity) + GX.entityHeight(centity) - GX.entityCollisionOffsetTop(eid)); + } else { + GX.entityPos(eid, GX.entityX(eid), (tpos.y + 1) * GX.tilesetHeight() - GX.entityCollisionOffsetTop(eid)); + } + } + } + + if (!GX.entityApplyGravity(eid)) { + // Test downward movement + if (GX.entityVY(eid) > 0) { + testy = Math.round(GX.entityVY(eid) / GX.frameRate()); + if (testy < 1) { testy = 1; } + tmove = Math.round(_entityTestMove(eid, 0, testy, tpos, centity)); + if (tmove == 0) { + // stop the motion + GX.entityVY(eid, 0); + + // don't let the entity pass into the collision entity or tile + if (centity > 0) { + GX.entityPos(eid, GX.entityX(eid), GX.entityY(centity) + GX.entityCollisionOffsetTop(centity) - GX.entityHeight(eid) + GX.entityCollisionOffsetBottom(eid)); + } + if (tpos.y > -1) { + GX.entityPos(eid, GX.entityX(eid), tpos.y * GX.tilesetHeight() - GX.entityHeight(eid) + GX.entityCollisionOffsetBottom(eid)); + } + } + } + } else { + + // Apply gravity + testy = Math.round(GX.entityVY(eid) / GX.frameRate()); + if (testy < 1) { testy = 1; } + tmove = Math.round(_entityTestMove(eid, 0, testy, tpos, centity)); + if (tmove == 1) { + // calculate the number of seconds since the gravity started being applied + var t = (GX.frame() - _entities[eid-1].jumpstart) / GX.frameRate(); + // adjust the y velocity for gravity + var g = _gravity * t ** 2 / 2; + if (g < 1) { g = 1; } + _entities[eid-1].vy = GX.entityVY(eid) + g; + if (GX.entityVY(eid) > _terminal_velocity) { GX.entityVY(eid, _terminal_velocity); } + + } else if (GX.entityVY(eid) >= 0) { + //alert("STOP"); + _entities[eid-1].jumpstart = GX.frame(); + if (GX.entityVY(eid) != 0) { + GX.entityVY(eid, 0); + + // don't let the entity fall through the collision entity or tile + if (centity > 0) { + GX.entityPos(eid, GX.entityX(eid), GX.entityY(centity) + GX.entityCollisionOffsetTop(centity) - GX.entityHeight(eid) + GX.entityCollisionOffsetBottom(eid)); + } + else { + //alert("pos: " + eid + ":" + tpos.y); + GX.entityPos(eid, GX.entityX(eid), tpos.y * GX.tilesetHeight() - GX.entityHeight(eid) + GX.entityCollisionOffsetBottom(eid)); + } + } + } + } + + if (GX.entityVX(eid) > 0) { + // Test right movement + testx = Math.round(GX.entityVX(eid) / GX.frameRate()); + if (testx < 1) { testx = 1 }; + tmove = Math.round(_entityTestMove(eid, testx, 0, tpos, centity)); + if (tmove == 0) { + // stop the motion + GX.entityVX(eid, 0); + + // don't let the entity pass into the collision entity or tile + if (centity > 0) { + GX.entityPos(eid, GX.entityX(centity) + GX.entityCollisionOffsetLeft(centity) - GX.entityWidth(eid) + GX.entityCollisionOffsetRight(eid), GX.entityY(eid)); + } + if (tpos.x > -1) { + GX.entityPos(eid, tpos.x * GX.tilesetWidth() - GX.entityWidth(eid) + GX.entityCollisionOffsetRight(eid), GX.entityY(eid)); + } + } + + } else if (GX.entityVX(eid) < 0) { + // Test left movement + testx = Math.round(GX.entityVX(eid) / GX.frameRate()); + if (testx > -1) { testx = -1 }; + tmove = Math.round(_entityTestMove(eid, testx, 0, tpos, centity)); + if (tmove == 0) { + // stop the motion + GX.entityVX(eid, 0); + + // don't let the entity pass into the collision entity or tile + if (centity > 0) { + GX.entityPos(eid, GX.entityX(centity) + GX.entityWidth(centity) - GX.entityCollisionOffsetRight(centity) - GX.entityCollisionOffsetLeft(eid), GX.entityY(eid)); + } + if (tpos.x > -1) { + GX.entityPos(eid, (tpos.x + 1) * GX.tilesetWidth() - GX.entityCollisionOffsetLeft(eid), GX.entityY(eid)); + } + } + } + } + + function _entityTestMove (entity, mx, my, tpos, collisionEntity) { + tpos.x = -1; + tpos.y = -1; + + var tcount = 0; + var tiles = []; + _entityCollisionTiles(entity, mx, my, tiles, tcount); + + var move = 1; + + // Test for tile collision + var tile = 0; + //for (var i = 0; i < tcount; i++) { + for (var i = 0; i < tiles.length; i++) { + var e = {}; + e.entity = entity; + e.event = GX.EVENT_COLLISION_TILE; + e.collisionTileX = tiles[i].x; + e.collisionTileY = tiles[i].y; + e.collisionResult = false; + + _onGameEvent(e); + if (e.collisionResult) { + move = 0; + //tpos = tiles[i]; + tpos.x = tiles[i].x; + tpos.y = tiles[i].y; + } + } + + // Test for entity collision + var entities = []; + var ecount = _entityCollision(entity, mx, my, entities); + for (var i=0; i < ecount; i++) { + var e = {}; + e.entity = entity; + e.event = GX.EVENT_COLLISION_ENTITY; + e.collisionEntity = entities[i]; + e.collisionResult = false; + _onGameEvent(e); + if (e.collisionResult) { + move = 0; + collisionEntity = entities[i]; + } + } + + return move; + } + + + function _entityCollision(eid, movex, movey, entities) { + var ecount = 0; + + for (var i = 1; i <= _entities.length; i++) { + if (i != eid) { + // TODO: only include entities that should be considered (e.g. visible, non-screen-level) + if (_rectCollide(GX.entityX(eid) + GX.entityCollisionOffsetLeft(eid) + movex, + GX.entityY(eid) + GX.entityCollisionOffsetTop(eid) + movey, + GX.entityWidth(eid) - GX.entityCollisionOffsetLeft(eid) - GX.entityCollisionOffsetRight(eid) - 1, + GX.entityHeight(eid) - GX.entityCollisionOffsetTop(eid) - GX.entityCollisionOffsetBottom(eid) - 1, + GX.entityX(i) + GX.entityCollisionOffsetLeft(i), + GX.entityY(i) + GX.entityCollisionOffsetTop(i), + GX.entityWidth(i) - GX.entityCollisionOffsetLeft(i) - GX.entityCollisionOffsetRight(i) - 1, + GX.entityHeight(i) - GX.entityCollisionOffsetTop(i) - GX.entityCollisionOffsetBottom(i)-1)) { + ecount = ecount + 1; + entities.push(i); + } + } + } + return ecount; + } + + function _entityCollisionTiles(entity, movex, movey, tiles, tcount) { + var tx = 0; // INTEGER + var ty = 0; // INTEGER + var tx0 = 0; // INTEGER + var txn = 0; // INTEGER + var ty0 = 0; // INTEGER + var tyn = 0; // INTEGER + var x = 0; // INTEGER + var y = 0; // INTEGER + var i = 0; // INTEGER + //var tiles = []; + + if (movex != 0) { + var startx = Math.round(-1 + GX.entityCollisionOffsetLeft(entity)); + if (movex > 0) { + startx = Math.round(GX.entityWidth(entity) + movex - GX.entityCollisionOffsetRight(entity)); + } + tx = Math.floor((GX.entityX(entity) + startx) / GX.tilesetWidth()) + + // This is a real brute force way to find the intersecting tiles. + // We're basically testing every pixel along the edge of the entity's + // collision rect and incrementing the collision tile count. + // With a bit more math I'm sure we could avoid some extra loops here. + tcount = 0; + ty0 = 0; + for (y = GX.entityY(entity) + GX.entityCollisionOffsetTop(entity); y <= GX.entityY(entity) + GX.entityHeight(entity) - 1 - GX.entityCollisionOffsetBottom(entity); y++) { + ty = Math.floor(y / GX.tilesetHeight()); + if (tcount == 0) { ty0 = ty; } + if (ty != tyn) { + tcount = tcount + 1; + } + tyn = ty; + } + + // Add the range of detected tile positions to the return list + for (ty = ty0; ty <= tyn; ty++) { + tiles.push({ + x: tx, + y: ty + }); + } + } + + if (movey != 0) { + var starty = Math.round(-1 + GX.entityCollisionOffsetTop(entity)); + if (movey > 0) { + starty = Math.round(GX.entityHeight(entity) + movey - GX.entityCollisionOffsetBottom(entity)); + } + ty = Math.floor((GX.entityY(entity) + starty) / GX.tilesetHeight()); + + // This is a real brute force way to find the intersecting tiles. + // We're basically testing every pixel along the edge of the entity's + // collision rect and incrementing the collision tile count. + // With a bit more math I'm sure we could avoid some extra loops here. + tcount = 0; + tx0 = 0; + for (x = GX.entityX(entity) + GX.entityCollisionOffsetLeft(entity); x <= GX.entityX(entity) + GX.entityWidth(entity) - 1 - GX.entityCollisionOffsetRight(entity); x++) { + tx = Math.floor(x / GX.tilesetWidth()); + if (tcount == 0) { tx0 = tx; } + if (tx != txn) { + tcount = tcount + 1; + } + txn = tx; + } + + for (tx = tx0; tx <= txn; tx++) { + tiles.push({ + x: tx, + y: ty + }) + } + } + } + + function _fullScreen(fullscreenFlag) { + if (fullscreenFlag != undefined) { + _fullscreenFlag = fullscreenFlag; + if (fullscreenFlag) { + document.getElementById("gx-container").requestFullscreen(); + } else { + document.exitFullscreen(); + } + } + return _fullscreenFlag; + } + + + + // Bitmap Font Methods + // ---------------------------------------------------------------------------- + function _fontCreate(filename, charWidth, charHeight, charref) { + var font = { + // Create a new game entity + eid: GX.entityCreate(filename, charWidth, charHeight, 1), + charSpacing: 0, + lineSpacing: 0 + }; + + // Hide the entity as we will not be displaying it as a normal sprite + GX.entityVisible(font.eid, false); + _fonts.push(font); + _font_charmap.push(new Array(256).fill({x:0,y:0})); + var fid = _fonts.length; + + // map the character codes to the image location + _fontMapChars(fid, charref); + + return fid; + } +/* + Sub GXFontCreate (filename As String, charWidth As Integer, charHeight As Integer, charref As String, uid As String) + Dim fid As Integer + fid = GXFontCreate(filename, charWidth, charHeight, charref) + __GX uid, fid, GXTYPE_FONT + End Sub +*/ + function _fontWidth (fid) { + return GX.entityWidth(_fonts[fid-1].eid); + } + + function _fontHeight (fid) { + return GX.entityHeight(_fonts[fid-1].eid); + } + + function _fontCharSpacing (fid, charSpacing) { + if (charSpacing != undefined) { + _fonts[fid-1].charSpacing = charSpacing; + } + return _fonts[fid-1].charSpacing; + } + + function _fontLineSpacing (fid, lineSpacing) { + if (lineSpacing != undefined) { + _fonts[fid-1].lineSpacing = lineSpacing; + } + return _fonts[fid-1].lineSpacing; + } + + function _drawText (fid, sx, sy, s) { + if (s == undefined) { return; } + //alert(fid); + var x = sx; + var y = sy; + var font = _fonts[fid-1]; + var e = _entities[font.eid-1]; + + for (var i = 0; i < s.length; i++) { + var a = s.charCodeAt(i); + if (a == 10) { // Line feed, move down to the next line + x = sx; + y = y + e.height + font.lineSpacing; + } else if (a != 13) { // Ignore Carriage Return + if (a != 32) { // Space character, nothing to draw + var cpos = _font_charmap[fid-1][a]; + GX.spriteDraw(e.image, x, y, cpos.y, cpos.x, e.width, e.height); //, __gx_scene.image '0 + } + x = x + e.width + font.charSpacing; + } + } + } + + function _fontMapChars (fid, charref) { + var cx = 1; + var cy = 1; + for (var i = 0; i < charref.length; i++) { + var a = charref.charCodeAt(i); + if (a == 10) { + cx = 1; + cy = cy + 1; + } else { + if (a >= 33 && a <= 256) { + _font_charmap[fid-1][a] = {x: cx, y: cy}; + } + cx = cx + 1; + } + } + } + + function _fontCreateDefault (fid) { + var filename = null; + if (fid == GX.FONT_DEFAULT_BLACK) { + filename = "gx/__gx_font_default_black.png"; + } else { + filename = "gx/__gx_font_default.png"; + } + + _fonts[fid-1].eid = GX.entityCreate(filename, 6, 8, 1); + GX.entityVisible(_fonts[fid-1].eid, false); + _fontMapChars(fid, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~`!@#$%^&*()_-+={}[]|\\,./<>?:;\"'"); + GX.fontLineSpacing(fid, 1); + } + + // Input Device Methods + // ----------------------------------------------------------------- + function _mouseInput() { + // TODO: need to decide whether to keep this here + // it is not needed for GX only to support QB64 + var mi = _mouseInputFlag; + _mouseInputFlag = false; + return mi; + } + + function _mouseX() { + return _mousePos.x; + } + + function _mouseY() { + return _mousePos.y; + }; + + function _mouseButton(button) { + // TODO: need to decide whether to keep this here + // it is not needed for GX - only to support QB64 + return _mouseButtons[button-1]; + }; + + function _deviceInputTest(di) { + if (di.deviceType = GX.DEVICE_KEYBOARD) { + if (di.inputType = GX.DEVICE_BUTTON) { + return GX.keyDown(di.inputId); + } + } + return false; + } + +/* + Function GXDeviceInputTest% (di As GXDeviceInput) + Dim dcount As Integer + dcount = _Devices + + If di.deviceId < 1 Or di.deviceId > dcount Then + GXDeviceInputTest = GX_FALSE + Exit Function + End If + + Dim result As Integer + Dim dactive As Integer + dactive = _DeviceInput(di.deviceId) + + If di.inputType = GXDEVICE_BUTTON Then + $If WIN Then + If _Button(di.inputId) = di.inputValue Then + result = GX_TRUE + End If + $Else + If di.deviceType = GXDEVICE_KEYBOARD Then + result = __GX_DeviceKeyDown(di.inputId) + Else + If _Button(di.inputId) = di.inputValue Then + result = GX_TRUE + End If + End If + $End If + + ElseIf di.inputType = GXDEVICE_AXIS Then + If _Axis(di.inputId) = di.inputValue Then + result = GX_TRUE + End If + End If + + GXDeviceInputTest = result + End Function + + $If LINUX OR MAC Then + Function __GX_DeviceKeyDown% (inputId As Integer) + Dim k As KeyEntry + k = __gx_keymap(inputId) + + Dim result As Integer + result = GX_FALSE + If _KeyDown(k.value) Then + result = GX_TRUE + ElseIf k.shift <> 0 Then + If _KeyDown(k.shift) Then result = GX_TRUE + End If + + __GX_DeviceKeyDown = result + End Function + $End If +*/ + function _keyInput (k, di) { + di.deviceId = GX.DEVICE_KEYBOARD; + di.deviceType = GX.DEVICE_KEYBOARD; + di.inputType = GX.DEVICE_BUTTON; + di.inputId = k; + di.inputValue = -1; + } +/* + Function GXKeyDown% (k As Long) + Dim di As GXDeviceInput + GXKeyInput k, di + GXKeyDown = GXDeviceInputTest(di) + End Function + + Sub GXDeviceInputDetect (di As GXDeviceInput) + Dim found As Integer + Dim dcount As Integer + dcount = _Devices + + While _DeviceInput + ' Flush the input buffer + Wend + + Do + _Limit 90 + Dim x As Integer + x = _DeviceInput + If x Then + Dim i As Integer + For i = 1 To _LastButton(x) + If _Button(i) Then + di.deviceId = x + di.deviceType = __GX_DeviceTypeName(x) + di.inputType = GXDEVICE_BUTTON + di.inputId = i + di.inputValue = _Button(i) + found = 1 + Exit Do + End If + Next i + + For i = 1 To _LastAxis(x) + If _Axis(i) And Abs(_Axis(i)) = 1 Then + di.deviceId = x + di.deviceType = __GX_DeviceTypeName(x) + di.inputType = GXDEVICE_AXIS + di.inputId = i + di.inputValue = _Axis(i) + found = 1 + Exit Do + End If + Next i + + For i = 1 To _LastWheel(x) + If _Wheel(i) Then + di.deviceId = x + di.deviceType = __GX_DeviceTypeName(x) + di.inputType = GXDEVICE_WHEEL + di.inputId = i + di.inputValue = _Wheel(i) + found = 1 + Exit Do + End If + Next i + End If + + $If LINUX OR MAC Then + ' No device input found, as a workaround let's loop through the key map looking for a keydown + For i = UBound(__gx_keymap) To 1 Step -1 + Dim keyIsDown As Integer, inputId As Integer + keyIsDown = GX_FALSE + If __gx_keymap(i).value <> 0 Then + 'If i > 29 Then + ' Print i; __gx_keymap(i).value + ' Dim x: Input x + 'End If + If _KeyDown(__gx_keymap(i).value) Then + keyIsDown = GX_TRUE + inputId = i + ElseIf __gx_keymap(i).shift <> 0 Then + If _KeyDown(__gx_keymap(i).shift) Then + keyIsDown = GX_TRUE + inputId = i + End If + End If + End If + If keyIsDown Then + di.deviceId = GXDEVICE_KEYBOARD + di.deviceType = __GX_DeviceTypeName(GXDEVICE_KEYBOARD) + di.inputType = GXDEVICE_BUTTON + di.inputId = inputId + di.inputValue = GX_TRUE + found = 1 + Exit Do + End If + Next i + $End If + Loop Until found + + While _DeviceInput + ' Flush the device input buffer + Wend + _KeyClear + + End Sub + + Function __GX_DeviceTypeName% (deviceId) + Dim dname As String + dname = _Device$(deviceId) + + If InStr(dname, "[KEYBOARD]") Then + __GX_DeviceTypeName = GXDEVICE_KEYBOARD + ElseIf InStr(dname, "[MOUSE]") Then + __GX_DeviceTypeName = GXDEVICE_MOUSE + ElseIf InStr(dname, "[CONTROLLER]") Then + __GX_DeviceTypeName = GXDEVICE_CONTROLLER + End If + End Function + + Function GXDeviceName$ (deviceId As Integer) + Dim nstart As Integer, nend As Integer + Dim dname As String + dname = _Device$(deviceId) + If InStr(dname, "[CONTROLLER]") Then + nstart = InStr(dname, "[NAME]") + If nstart = 0 Then + dname = "Controller" + Else + nstart = nstart + 7 + nend = InStr(nstart, dname, "]]") + dname = _Trim$(Mid$(dname, nstart, nend - nstart)) + End If + ElseIf InStr(dname, "[MOUSE]") Then + dname = "Mouse" + ElseIf InStr(dname, "[KEYBOARD]") Then + dname = "Keyboard" + End If + GXDeviceName = dname + End Function + + Function GXDeviceTypeName$ (dtype As Integer) + Dim dtypename As String + Select Case dtype + Case GXDEVICE_KEYBOARD: dtypename = "KEYBOARD" + Case GXDEVICE_MOUSE: dtypename = "MOUSE" + Case GXDEVICE_CONTROLLER: dtypename = "CONTROLLER" + End Select + GXDeviceTypeName = dtypename + End Function + + Function GXInputTypeName$ (itype As Integer) + Dim itypename As String + Select Case itype + Case GXDEVICE_BUTTON: itypename = "BUTTON" + Case GXDEVICE_AXIS: itypename = "AXIS" + Case GXDEVICE_WHEEL: itypename = "WHEEL" + End Select + GXInputTypeName = itypename + End Function +*/ + function _keyButtonName (inputId ) { + var k; + switch (inputId) { + case GX.KEY_ESC: k = "Esc"; break; + case GX.KEY_1: k = "1"; break; + case GX.KEY_2: k = "2"; break; + case GX.KEY_3: k = "3"; break; + case GX.KEY_4: k = "4"; break; + case GX.KEY_5: k = "5"; break; + case GX.KEY_6: k = "6"; break; + case GX.KEY_7: k = "7"; break; + case GX.KEY_8: k = "8"; break; + case GX.KEY_9: k = "9"; break; + case GX.KEY_0: k = "0"; break; + case GX.KEY_DASH: k = "-"; break; + case GX.KEY_EQUALS: k = "="; break; + case GX.KEY_BACKSPACE: k = "Bksp"; break; + case GX.KEY_TAB: k = "Tab"; break; + case GX.KEY_Q: k = "Q"; break; + case GX.KEY_W: k = "W"; break; + case GX.KEY_E: k = "E"; break; + case GX.KEY_R: k = "R"; break; + case GX.KEY_T: k = "T"; break; + case GX.KEY_Y: k = "Y"; break; + case GX.KEY_U: k = "U"; break; + case GX.KEY_I: k = "I"; break; + case GX.KEY_O: k = "O"; break; + case GX.KEY_P: k = "P"; break; + case GX.KEY_LBRACKET: k = "["; break; + case GX.KEY_RBRACKET: k = "]"; break; + case GX.KEY_ENTER: k = "Enter"; break; + case GX.KEY_LCTRL: k = "LCtrl"; break; + case GX.KEY_A: k = "A"; break; + case GX.KEY_S: k = "S"; break; + case GX.KEY_D: k = "D"; break; + case GX.KEY_F: k = "F"; break; + case GX.KEY_G: k = "G"; break; + case GX.KEY_H: k = "H"; break; + case GX.KEY_J: k = "J"; break; + case GX.KEY_K: k = "K"; break; + case GX.KEY_L: k = "L"; break; + case GX.KEY_SEMICOLON: k = ";"; break; + case GX.KEY_QUOTE: k = "'"; break; + case GX.KEY_BACKQUOTE: k = "`"; break; + case GX.KEY_LSHIFT: k = "LShift"; break; + case GX.KEY_BACKSLASH: k = "\\"; break; + case GX.KEY_Z: k = "Z"; break; + case GX.KEY_X: k = "X"; break; + case GX.KEY_C: k = "C"; break; + case GX.KEY_V: k = "V"; break; + case GX.KEY_B: k = "B"; break; + case GX.KEY_N: k = "N"; break; + case GX.KEY_M: k = "M"; break; + case GX.KEY_COMMA: k = ","; break; + case GX.KEY_PERIOD: k = "."; break; + case GX.KEY_SLASH: k = "/"; break; + case GX.KEY_RSHIFT: k = "RShift"; break; + case GX.KEY_NUMPAD_MULTIPLY: k = "NPad *"; break; + case GX.KEY_SPACEBAR: k = "Space"; break; + case GX.KEY_CAPSLOCK: k = "CapsLk"; break; + case GX.KEY_F1: k = "F1"; break; + case GX.KEY_F2: k = "F2"; break; + case GX.KEY_F3: k = "F3"; break; + case GX.KEY_F4: k = "F4"; break; + case GX.KEY_F5: k = "F5"; break; + case GX.KEY_F6: k = "F6"; break; + case GX.KEY_F7: k = "F7"; break; + case GX.KEY_F8: k = "F8"; break; + case GX.KEY_F9: k = "F9"; break; + case GX.KEY_PAUSE: k = "Pause"; break; + case GX.KEY_SCRLK: k = "ScrLk"; break; + case GX.KEY_NUMPAD_7: k = "NPad 7"; break; + case GX.KEY_NUMPAD_8: k = "NPad 8"; break; + case GX.KEY_NUMPAD_9: k = "NPad 9"; break; + case GX.KEY_NUMPAD_MINUS: k = "-"; break; + case GX.KEY_NUMPAD_4: k = "NPad 4"; break; + case GX.KEY_NUMPAD_5: k = "NPad 5"; break; + case GX.KEY_NUMPAD_6: k = "NPad 6"; break; + case GX.KEY_NUMPAD_PLUS: k = "+"; break; + case GX.KEY_NUMPAD_1: k = "NPad 1"; break; + case GX.KEY_NUMPAD_2: k = "NPad 2"; break; + case GX.KEY_NUMPAD_3: k = "NPad 3"; break; + case GX.KEY_NUMPAD_0: k = "NPad 0"; break; + case GX.KEY_NUMPAD_PERIOD: k = "NPad ."; break; + case GX.KEY_F11: k = "F11"; break; + case GX.KEY_F12: k = "F12"; break; + case GX.KEY_NUMPAD_ENTER: k = "NPad Enter"; break; + case GX.KEY_RCTRL: k = "RCtrl"; break; + case GX.KEY_NUMPAD_DIVIDE: k = "NPad /"; break; + case GX.KEY_NUMLOCK: k = "NumLk"; break; + case GX.KEY_HOME: k = "Home"; break; + case GX.KEY_UP: k = "Up"; break; + case GX.KEY_PAGEUP: k = "PgUp"; break; + case GX.KEY_LEFT: k = "Left"; break; + case GX.KEY_RIGHT: k = "Right"; break; + case GX.KEY_END: k = "End"; break; + case GX.KEY_DOWN: k = "Down"; break; + case GX.KEY_PAGEDOWN: k = "PgDn"; break; + case GX.KEY_INSERT: k = "Ins"; break; + case GX.KEY_DELETE: k = "Del"; break; + case GX.KEY_LWIN: k = "LWin"; break; + case GX.KEY_RWIN: k = "RWin"; break; + case GX.KEY_MENU: k = "Menu"; break; + } + return k; + } + + + + // Debugging Methods + // -------------------------------------------------------------------------- + function _debug(enabled) { + if (enabled != undefined) { + __debug.enabled = enabled; + } + return __debug.enabled; + } + +/* + Function GXDebugScreenEntities + GXDebugScreenEntities = __gx_debug.screenEntities + End Function + + Sub GXDebugScreenEntities (enabled As Integer) + __gx_debug.screenEntities = enabled + End Sub +*/ + function _debugFont(font) { + if (font != undefined) { + __debug.font = font; + } + return __debug.font; + } + + function _debugTileBorderColor(c) { + if (c != undefined) { + _debug.tileBorderColor = c; + } + return _debug.tileBorderColor; + } + + function _debugEntityBorderColor(c) { + if (c != undefined) { + _debug.entityBorderColor = c; + } + return _debug.entityBorderColor; + } + + function _debugEntityCollisionColor(c) { + if (c != undefined) { + _debug.entityCollisionColor = c; + } + return _debug.entityCollisionColor; + } + + /* + Sub __GX_DebugMapTile + Dim t As Integer, tx As Long, ty As Long, depth As Integer, i As Integer + Dim tpos As GXPosition + '__GX_MapTilePosAt _MOUSEX, _MOUSEY, tpos + GXMapTilePosAt GXMouseX, GXMouseY, tpos + depth = GXMapTileDepth(tpos.x, tpos.y) + tx = tpos.x * GXTilesetWidth - GXSceneX + ty = tpos.y * GXTilesetHeight - GXSceneY + + 'Dim cdest As Long + 'cdest = _Dest + '_Dest __gx_scene.image + Line (tx, ty)-(tx + GXTilesetWidth - 1, ty + GXTilesetHeight - 1), GXDebugTileBorderColor, B , &B1010101010101010 + GXDrawText GXDebugFont, tx, ty - 8, "(" + _Trim$(Str$(tpos.x)) + "," + _Trim$(Str$(tpos.y)) + ")" + For i = 1 To depth + t = GXMapTile(tpos.x, tpos.y, i) + GXDrawText GXDebugFont, tx, ty + GXTilesetHeight + 1 + (i - 1) * 8, _Trim$(Str$(i)) + ":" + _Trim$(Str$(t)) + Next i + + '_Dest cdest + End Sub + + Sub __GX_DebugEntity (ent As GXEntity, x, y) + If ent.screen And Not GXDebugScreenEntities Then Exit Sub + + 'Dim odest As Long + 'odest = _Dest + '_Dest __gx_scene.image + + ' Display the entity's position + GXDrawText GXDebugFont, x, y - 8, "(" + __GX_DebugRound(ent.x, 2) + "," + __GX_DebugRound(ent.y, 2) + ")" + + ' Draw the entity's bounding rect + Line (x, y)-(x + ent.width - 1, y + ent.height - 1), GXDebugEntityBorderColor, B , &B1010101010101010 + + ' Draw the entity's collision rect + Line (x + ent.coLeft, y + ent.coTop)-(x + ent.width - 1 - ent.coRight, y + ent.height - 1 - ent.coBottom), GXDebugEntityCollisionColor, B ', &B1010101010101010 + + '_Dest odest + End Sub + + Function __GX_DebugRound$ (n As Double, decimalPlaces As Integer) + Dim n2 As Long + n2 = _Round(n * 10 ^ decimalPlaces) + If n2 = 0 Then + __GX_DebugRound = "0." + String$(decimalPlaces, "0") + Else + Dim ns As String, decimal As String + ns = _Trim$(Str$(n2)) + decimal = Right$(ns, decimalPlaces) + ns = Left$(ns, Len(ns) - decimalPlaces) + __GX_DebugRound = ns + "." + decimal + End If + End Function +*/ + function _debugFrameRate() { + var frame = String(GX.frame()); + var frameRate = String(GX.frameRate()); + frameRate = frameRate.padStart(frame.length - frameRate.length, " "); //+ frameRate + + GX.drawText(GX.debugFont(), GX.sceneWidth() - (frame.length + 6) * 6 - 1, 1, "FRAME:" + frame); + GX.drawText(GX.debugFont(), GX.sceneWidth() - (frameRate.length + 4) * 6 - 1, 9, "FPS:" + frameRate); + } + + function _sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + }; + + function _init() { + // init + _fontCreateDefault(GX.FONT_DEFAULT); + _fontCreateDefault(GX.FONT_DEFAULT_BLACK); + + // keyboard event initialization + // detect key state for KeyDown method + addEventListener("keyup", function(event) { + if (_scene.active) { + event.preventDefault(); + } + _pressedKeys[event.keyCode] = false; + }); + addEventListener("keydown", function(event) { + if (_scene.active) { + event.preventDefault(); + } + _pressedKeys[event.keyCode] = true; + }); + } + + this.ctx = function() { return _ctx; }; + this.canvas = function() { return _canvas; }; + + this.frame = _frame; + this.frameRate = _frameRate; + + this.sceneColumns = _sceneColumns; + this.sceneConstrain = _sceneConstrain; + this.sceneCreate = _sceneCreate; + this.sceneDraw = _sceneDraw; + this.sceneFollowEntity = _sceneFollowEntity; + this.sceneHeight = _sceneHeight; + this.sceneMove = _sceneMove; + this.scenePos = _scenePos; + this.sceneRows = _sceneRows; + this.sceneScale = _sceneScale; + this.sceneStart = _sceneStart; + this.sceneStop = _sceneStop; + this.sceneUpdate = _sceneUpdate; + this.sceneWidth = _sceneWidth; + this.sceneX = _sceneX; + this.sceneY = _sceneY; + + this.spriteDraw = _spriteDraw; + this.spriteDrawScaled = _spriteDrawScaled; + + this.backgroundAdd = _backgroundAdd; + this.backgroundY = _backgroundY; + this.backgroundHeight = _backgroundHeight; + this.backgroundClear = _backgroundClear; + + this.soundLoad = _soundLoad; + this.soundPlay = _soundPlay; + this.soundRepeat = _soundRepeat; + this.soundPause = _soundPause; + this.soundStop = _soundStop; + this.soundVolumne = _soundVolume; + this.soundMuted = _soundMuted; + + this.entityCreate = _entityCreate; + this.screenEntityCreate = _screenEntityCreate; + this.entityAnimate = _entityAnimate; + this.entityAnimateStop = _entityAnimateStop; + this.entityAnimateMode = _entityAnimateMode; + this.entityX = _entityX; + this.entityY = _entityY; + this.entityWidth = _entityWidth; + this.entityHeight = _entityHeight; + this.entityMove = _entityMove; + this.entityPos = _entityPos; + this.entityVX = _entityVX; + this.entityVY = _entityVY; + this.entityFrameNext = _entityFrameNext; + this.entityFrameSet = _entityFrameSet; + this.entityType = _entityType; + this.entityCollisionOffset = _entityCollisionOffset; + this.entityCollisionOffsetLeft = _entityCollisionOffsetLeft; + this.entityCollisionOffsetTop = _entityCollisionOffsetTop; + this.entityCollisionOffsetRight = _entityCollisionOffsetRight; + this.entityCollisionOffsetBottom = _entityCollisionOffsetBottom; + this.entityApplyGravity = _entityApplyGravity; + this.entityVisible = _entityVisible; + + this.mapColumns = _mapColumns; + this.mapCreate = _mapCreate; + this.mapLoad = _mapLoad; + this.mapDraw = _mapDraw; + this.mapIsometric = _mapIsometric; + this.mapLayerAdd = _mapLayerAdd; + this.mapLayerInit = _mapLayerInit; + this.mapLayerVisible = _mapLayerVisible; + this.mapLayers = _mapLayers; + this.mapRows = _mapRows; + this.mapTile = _mapTile; + + this.tilesetColumns = _tilesetColumns; + this.tilesetCreate = _tilesetCreate; + this.tilesetFilename = _tilesetFilename; + this.tilesetHeight = _tilesetHeight; + this.tilesetImage = _tilesetImage; + this.tilesetPos = _tilesetPos; + this.tilesetRows = _tilesetRows; + this.tilesetWidth = _tilesetWidth; + this.tilesetReplaceImage = _tilesetReplaceImage; + + this.fontCharSpacing = _fontCharSpacing; + this.fontCreate = _fontCreate; + this.fontHeight = _fontHeight; + this.fontLineSpacing = _fontLineSpacing; + this.fontWidth = _fontWidth; + this.drawText = _drawText; + + this.deviceInputTest = _deviceInputTest; + this.keyInput = _keyInput; + this.keyButtonName = _keyButtonName; + this.mouseX = _mouseX; + this.mouseY = _mouseY; + this.mouseButton = _mouseButton; + this._mouseInput = _mouseInput; + + this.debug = _debug; + this.debugFont = _debugFont; + this.debugEntityBorderColor = _debugEntityBorderColor; + this.debugEntityCollisionColor = _debugEntityCollisionColor; + this.debugTileBorderColor = _debugTileBorderColor; + + this.fullScreen = _fullScreen; + this.keyDown = _keyDown; + + this.init = _init; + this.reset = _reset; + this.sleep = _sleep; + this.registerGameEvents = _registerGameEvents; + this.resourcesLoaded = _resourcesLoaded; + + this.sceneActive = function() { return _scene.active; } + + // constants + this.TRUE = true; + this.FALSE = false; + + this.EVENT_INIT = 1; + this.EVENT_UPDATE = 2; + this.EVENT_DRAWBG = 3; + this.EVENT_DRAWMAP = 4; + this.EVENT_DRAWSCREEN = 5; + this.EVENT_MOUSEINPUT = 6; + this.EVENT_PAINTBEFORE = 7; + this.EVENT_PAINTAFTER = 8; + this.EVENT_COLLISION_TILE = 9; + this.EVENT_COLLISION_ENTITY = 10; + this.EVENT_PLAYER_ACTION = 11; + this.EVENT_ANIMATE_COMPLETE = 12; + + this.ANIMATE_LOOP = 0; + this.ANIMATE_SINGLE = 1; + + this.BG_STRETCH = 1; + this.BG_SCROLL = 2; + this.BG_WRAP = 3; + + + this.KEY_ESC = 27; + this.KEY_1 = 49; + this.KEY_2 = 50; + this.KEY_3 = 51; + this.KEY_4 = 52; + this.KEY_5 = 53; + this.KEY_6 = 54; + this.KEY_7 = 55; + this.KEY_8 = 56; + this.KEY_9 = 57; + this.KEY_0 = 48; + this.KEY_DASH = 189; + this.KEY_EQUALS = 187; + this.KEY_BACKSPACE = 8; + this.KEY_TAB = 9; + this.KEY_Q = 81; + this.KEY_W = 87; + this.KEY_E = 69; + this.KEY_R = 82; + this.KEY_T = 84; + this.KEY_Y = 89; + this.KEY_U = 85; + this.KEY_I = 73; + this.KEY_O = 79; + this.KEY_P = 80; + this.KEY_LBRACKET = 219; + this.KEY_RBRACKET = 221; + this.KEY_ENTER = 13; + this.KEY_LCTRL = 17; + this.KEY_A = 65; + this.KEY_S = 83; + this.KEY_D = 68; + this.KEY_F = 70; + this.KEY_G = 71; + this.KEY_H = 72; + this.KEY_J = 74; + this.KEY_K = 75; + this.KEY_L = 76; + this.KEY_SEMICOLON = 186; + this.KEY_QUOTE = 222; + this.KEY_BACKQUOTE = 192; + this.KEY_LSHIFT = 16; + this.KEY_BACKSLASH = 220; + this.KEY_Z = 90; + this.KEY_X = 88; + this.KEY_C = 67; + this.KEY_V = 86; + this.KEY_B = 66; + this.KEY_N = 78; + this.KEY_M = 77; + this.KEY_COMMA = 188; + this.KEY_PERIOD = 190; + this.KEY_SLASH = 191; + this.KEY_RSHIFT = 16; + this.KEY_NUMPAD_MULTIPLY = 106; + this.KEY_SPACEBAR = 32; + this.KEY_CAPSLOCK = 20; + this.KEY_F1 = 112; + this.KEY_F2 = 113; + this.KEY_F3 = 114; + this.KEY_F4 = 115; + this.KEY_F5 = 116; + this.KEY_F6 = 117; + this.KEY_F7 = 118; + this.KEY_F8 = 119; + this.KEY_F9 = 120; + this.KEY_F9 = 121; + this.KEY_PAUSE = 19; + this.KEY_SCRLK = 145; + this.KEY_NUMPAD_7 = 103; + this.KEY_NUMPAD_8 = 104; + this.KEY_NUMPAD_9 = 105; + this.KEY_NUMPAD_MINUS = 109; + this.KEY_NUMPAD_4 = 100; + this.KEY_NUMPAD_5 = 101; + this.KEY_NUMPAD_6 = 102; + this.KEY_NUMPAD_PLUS = 107; + this.KEY_NUMPAD_1 = 97; + this.KEY_NUMPAD_2 = 98; + this.KEY_NUMPAD_3 = 99; + this.KEY_NUMPAD_0 = 96; + this.KEY_NUMPAD_PERIOD = 110; + this.KEY_F11 = 122; + this.KEY_F12 = 123; + this.KEY_NUMPAD_ENTER = 13; + this.KEY_RCTRL = 17; + this.KEY_NUMPAD_DIVIDE = 111; + this.KEY_NUMLOCK = 144; + this.KEY_HOME = 36; + this.KEY_UP = 38; + this.KEY_PAGEUP = 33; + this.KEY_LEFT = 37; + this.KEY_RIGHT = 39; + this.KEY_END = 35; + this.KEY_DOWN = 40; + this.KEY_PAGEDOWN = 34; + this.KEY_INSERT = 45; + this.KEY_DELETE = 46; + this.KEY_LWIN = 91; + this.KEY_RWIN = 92; + this.KEY_MENU = 93; + + this.ACTION_MOVE_LEFT = 1; + this.ACTION_MOVE_RIGHT = 2; + this.ACTION_MOVE_UP = 3; + this.ACTION_MOVE_DOWN = 4; + this.ACTION_JUMP = 5; + this.ACTION_JUMP_RIGHT = 6; + this.ACTION_JUMP_LEFT = 7; + + this.SCENE_FOLLOW_NONE = 0; // no automatic scene positioning (default) + this.SCENE_FOLLOW_ENTITY_CENTER = 1; // center the view on a specified entity + this.SCENE_FOLLOW_ENTITY_CENTER_X = 2; // center the x axis of the scene on the specified entity + this.SCENE_FOLLOW_ENTITY_CENTER_Y = 3; // center the y axis of the scene on the specified entity + this.SCENE_FOLLOW_ENTITY_CENTER_X_POS = 4; // center the x axis of the scene only when moving to the right + this.SCENE_FOLLOW_ENTITY_CENTER_X_NEG = 5; // center the x axis of the scene only when moving to the left + + this.SCENE_CONSTRAIN_NONE = 0; // no checking on scene position: can be negative, can exceed map size (default) + this.SCENE_CONSTRAIN_TO_MAP = 1; // do not allow screen position outside the bounds of the map size + + this.FONT_DEFAULT = 1; // default bitmap font (white) + this.FONT_DEFAULT_BLACK = 2; // default bitmap font (black + + this.DEVICE_KEYBOARD = 1; + this.DEVICE_MOUSE = 2; + this.DEVICE_CONTROLLER = 3; + this.DEVICE_BUTTON = 4; + this.DEVICE_AXIS = 5; + this.DEVICE_WHEEL = 6; + + this.TYPE_ENTITY = 1; + this.TYPE_FONT = 2; + + this.CR = "\r"; + this.LF = "\n"; + this.CRLF = "\r\n" +}; + + +// Consider moving these to separate optional js files +var GXSTR = new function() { + this.lPad = function(str, padChar, padLength) { + return String(str).padStart(padLength, padChar); + } + + this.rPad = function(str, padChar, padLength) { + return String(str).padEnd(padLength, padChar); + } + + this.replace = function(str, findStr, replaceStr) { + return String(str).replace(findStr, replaceStr); + } +}; + +GX.init(); diff --git a/img/fullscreen.png b/img/fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..87c4bfb0b5cdee1d540454b83a6164f86e98cc54 GIT binary patch literal 6250 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 literal 0 HcmV?d00001 diff --git a/img/gx-logo.png b/img/gx-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9da1be62fd4dff4c00428f6b0afbfa232acea5d9 GIT binary patch literal 1696 zcmV;R24DG!P)EX>4Tx04R}tkv&MmKpe$iQ%glEf@TnL$WWauh>AE$6^me@v=v%)FuC*#ni!H4 z7e~Rh;NZt%)xpJCR|i)?5c~jfb8}L3krMxx6k5c1aNLh~_a1le0HIN3n$4LokXvz#@`JL4txBDyYInj8>f#3u)Sq`}l`ke~MfRxhi1f zn8yY*$gUs!4}N!R6(=XWq(~eHy*SRt2oT-{nsvwdK6aeu2@re+uJpElqXEo(l3s6X z(IcR58@RY`YswyQxdRM78L}z6Qjn%lECTOm^i6qS;1=jv^?GaVRCg4|Kj+R9sI(Hg@4jZpb;mGr#lYynkZwgYpW=;1$zr0M3{C)zJp1uQB;2n{fNFy}m=>L<_iG zrfhlnO`ryJdJm3}RtwN79fv*lK zJvoEPG_1!UyM{RGbGj8(aIyz3221a83DVPN8V7XhF3dc&0pJz*v>iQp3~ls*(*|P# z9ByK^uXgE}{KH=u|8XlFawRu9HM+qb!q{yuc# zGSoSdKu_*+0L`MeMj(+b17NTkJ$|IbwoLqseE$bbr!CSXT;D`^fSjm8&ksUH2gLKv zZ1%&k254=8DvLK3XCOFNz7Ob`0JzDVp+gwnFpELf6Nt32qN4Z8#IAB4)9(zB*E=&FUub(;{R6w>KJsoM?Jj^__S z7NqfT`TpVZAVUMTbJt)w1JNQEIi1ksGZQkJL?TN&ABjX^!Y&D9EMZ&-Lu|w11wRb5 z+5m_v@~CK(ilF`7ChCjx$Y(#9-|T{^n*w9TS`{hu+F$6sn?OD(4Fh>+0QNY_FC;QL zRg%Gas001H{n;DMTn@wzh3~`o6BwR=)re(;H^Aw`HXG5e^TqA|u{eWx6uml@pO#s4Y@}dzNOv!^Ru=CnAALg_WHW;H9w_XHP6zl~&{F|8-2(47 z!C9vI54eMQ42pei??eC6o%JR*-{-z{tnHrXHfRun;S=lwOKpeETzUcN|hF z`@nC3FS#?J4s(cYmbpY7dZ3bt*i#;Iy_jh*$EXGjUl5UAXIqP>q9>L1RR;2 zxuGLL$n5gKnopqPw~?a*W&45+r?&M2&;pW^`_u-9pJ%dIS|ZoR}> q9?wEN_2R4imcml{_0#PC2jD*r4wY{h?BI|90000q!^2X+?^QKos)S9WS{qRaSW-L z^LFmVyu%7Su5&g2|3Cd^=^Ec%ddfH2p3M6k5~{V}gq_6YRo}{|w9>!if!D(>Q0nSgROie?Pj|<+_H1!yFIhkWD@l_8ymCd7#=>(Ugl# zK>aXC@0NE9FZRs)&%vYMw~SFMcpjf$LH9l;Hi7gOMlD6P8MWu<@&fG#>fTc~`$o0i z_j&Jy5*P~83>>mb@7z*9u}*W-^TP~}dQ1+eiOT+BxTyB>?cMh`>+3vx8ySyCCNjJ~ zbDz(y(!FroZZ5D5PR90)FKl;jUuMAycER*>e{O!u$v0@Qn8?;~uI4>s^zqaCqB#Q9 zfwG4frdvJV-ePqD^~T4shhc}Em8D&_&>nZeW5&t;ucLK6Vvnb%+d literal 0 HcmV?d00001 diff --git a/index.html b/index.html new file mode 100644 index 0000000..f123ef6 --- /dev/null +++ b/index.html @@ -0,0 +1,243 @@ + + + + + + + + + + + + +
+
+ Run Program + Stop + Share +
+
+
+
+
+
+
+
Show Javascript
+
+ + + + + + + \ No newline at end of file diff --git a/lp-dosvga.ttf b/lp-dosvga.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6aaf28c59b0f530d3599326f1345ffaafd956063 GIT binary patch literal 79584 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 KiGLnG75_ITQ-hiS literal 0 HcmV?d00001 diff --git a/qb.js b/qb.js new file mode 100644 index 0000000..03ecbbd --- /dev/null +++ b/qb.js @@ -0,0 +1,922 @@ +var QB = new function() { + // QB constants + this.COLUMN_ADVANCE = Symbol("COLUMN_ADVANCE"); + this.PREVENT_NEWLINE = Symbol("PREVENT_NEWLINE"); + + var _fgColor = null; + var _bgColor = null; + var _lastX = 0; + var _lastY = 0; + //var _fntDefault = null; + var _locX = 0; + var _locY = 0; + var _lastKey = null; + var _keyBuffer = []; + var _inputMode = false; + var _haltedFlag = false; + var _runningFlag = false; + + // Array handling methods + // ---------------------------------------------------- + this.initArray = function(dimensions, obj) { + var a = {}; + if (dimensions && dimensions.length > 0) { + a._dimensions = dimensions; + } + else { + // default to single dimension to support Dim myArray() syntax + // for convenient hashtable declaration + a._dimensions = [0]; + } + a._newObj = { value: obj }; + return a; + }; + + this.resizeArray = function(a, dimensions, obj, preserve) { + if (!preserve) { + var props = Object.getOwnPropertyNames(a); + for (var i = 0; i < props.length; i++) { + if (props[i] != "_newObj") { + delete a[props[i]]; + } + } + } + a._dimensions = dimensions; + }; + + this.arrayValue = function(a, indexes) { + var value = a; + for (var i=0; i < indexes.length; i++) { + if (value[indexes[i]] == undefined) { + if (i == indexes.length-1) { + value[indexes[i]] = JSON.parse(JSON.stringify(a._newObj)); + } + else { + value[indexes[i]] = {}; + } + } + value = value[indexes[i]]; + } + + // added to treat regular objects and qb hashtable/arrays the same + if (value.value == undefined) { + value = { value: value }; + } + return value; + }; + + this.import = async function(url) { + await fetch(url).then(response => response.text()).then((response) => { + var f = new Function(response); + f(); + }); + }; +/* + this.byRef = function(v) { + if (v != undefined && v.value == undefined) { + return { value: v }; + } + return v; + }; + + this.byValue = function(v) { + if (v !== undefined && v.value != undefined) { + return v.value; + } + return v; + }; + + this.$var = function(varname) { + return window[varname]; + }; + + this.$func = function(methodName) { + var func = window["sub_" + methodName]; + if (func == undefined) { + func = window["func_" + methodName] + } + return func; + } +*/ + // Process control methods + // ------------------------------------------- + this.halt = function() { + _haltedFlag = true; + _runningFlag = false; + }; + + this.halted = function() { + return _haltedFlag; + }; + + this.end = function() { + _runningFlag = false; + }; + + this.start = function() { + _runningFlag = true; + _haltedFlag = false; + } + + this.running = function() { + return _runningFlag; + }; + + + // Extended QB64 Keywords + // -------------------------------------------- + this.func__Alpha = function(rgb, imageHandle) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return _color(rgb).a * 255; + }; + + this.func__Alpha32 = function(rgb) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return _color(rgb).a * 255; + }; + + this.func__Atan2 = function(y, x) { + return Math.atan2(y, x); + }; + + this.func__Blue = function(rgb, imageHandle) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return _color(rgb).b; + }; + + this.func__Blue32 = function(rgb) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return _color(rgb).b; + }; + + this.sub__Delay = async function(seconds) { + await GX.sleep(seconds*1000); + }; + + this.func__FontHeight = function(fnt) { + //return GX.fontHeight(_fntDefault) + GX.fontLineSpacing(_fntDefault); + return 16; + }; + + this.func__FontWidth = function(fnt) { + //return GX.fontWidth(_fntDefault); + return 8; + }; + + this.func__Green = function(rgb, imageHandle) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return _color(rgb).g; + }; + + this.func__Green32 = function(rgb) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return _color(rgb).g; + }; + + this.func__Height = function(img) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return GX.sceneHeight(); + }; + + this.func__InStrRev = function(arg1, arg2, arg3) { + var startIndex = +Infinity; + var strSource = ""; + var strSearch = ""; + if (arg3 != undefined) { + startIndex = arg1-1; + strSource = String(arg2); + strSearch = String(arg3); + } + else { + strSource = String(arg1); + strSearch = String(arg2); + } + return strSource.lastIndexOf(strSearch, startIndex)+1; + }; + + this.func__KeyDown = function(keyCode) { + // TODO: actual implementation (maybe) + // this is here just to allow converted programs to compile + return GX.keyDown(keyCode) ? -1 : 0; + }; + this.func__KeyHit = function() { + // TODO: actual implementation (maybe) + // this is here just to support rendering loops that are using _KeyHit as the exit criteria + return 0; + }; + + this.sub__Limit = async function(fps) { + // TODO: limit based on frame rate + // need to incorporate time elapsed from last loop invocation + + await GX.sleep(1000/fps); + }; + + this.func__MouseInput = function() { + return GX._mouseInput(); + }; + + this.func__MouseX = function() { + return GX.mouseX(); + }; + + this.func__MouseY = function() { + return GX.mouseY(); + }; + + this.func__MouseButton = function(button) { + return GX.mouseButton(button); + }; + + this.func__NewImage = function(iwidth, iheight) { + return { + width: iwidth, + height: iheight + }; + }; + + this.sub__PrintString = function(x, y, s) { + // TODO: check the background opacity mode + // Draw the text background + var ctx = GX.ctx(); + ctx.beginPath(); + ctx.fillStyle = _bgColor.rgba(); + ctx.fillRect(x, y, QB.func__FontWidth(), QB.func__FontHeight()); + //GX.drawText(_fntDefault, x, y, s); + ctx.font = "16px dosvga"; + ctx.fillStyle = _fgColor.rgba(); + ctx.fillText(s, x, y+QB.func__FontHeight()-6); + }; + + this.func__PrintWidth = function(s) { + if (!s) { return 0; } + return String(s).length * QB.func__FontWidth(); + }; + + this.func__Pi = function(m) { + if (m == undefined) { + m = 1; + } + return Math.PI * m; + } + + function _rgb(r, g, b) { + return { + r: r, + g: g, + b: b, + a: 1, + rgb: function() { return "rgb(" + this.r + "," + this.g + "," + this.b + ")"; }, + rgba: function() { return "rgba(" + this.r + "," + this.g + "," + this.b + "," + this.a + ")"; } + } + } + + this.func__Red = function(rgb, imageHandle) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return _color(rgb).r; + }; + + this.func__Red32 = function(rgb) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return _color(rgb).r; + }; + + this.func__RGB = function(r, g, b) { + return this.func__RGB32(r, g, b); + }; + + this.func__RGB32 = function(r, g, b, a) { + if (a == undefined) { + a = 255; + } + if (b == undefined && g != undefined) { + a = g; + g = r; + b = r; + } + else if (b == undefined) { + g = r; + b = r; + } + a = a / 255; + + return { + r: r, + g: g, + b: b, + a: a, + rgb: function() { return "rgb(" + this.r + "," + this.g + "," + this.b + ")"; }, + rgba: function() { return "rgba(" + this.r + "," + this.g + "," + this.b + "," + this.a + ")"; } + } + } + + this.func__Round = function(value) { + return Math.round(value); + }; + + this.func__ScreenExists = function() { + return true; + } + + this.sub__Title = function(title) { + document.title = title; + }; + + this.func__Trim = function(value) { + return value.trim(); + }; + + this.func__Width = function(img) { + // TODO: implement corresponding logic when an image handle is supplied (maybe) + return GX.sceneWidth(); + }; + + + // QB45 Keywords + // -------------------------------------------- + this.func_Asc = function(value, pos) { + if (pos == undefined) { + pos = 0; + } + else { pos--; } + + return String(value).charCodeAt(pos); + } + + this.func_Abs = function(value) { + return Math.abs(value); + }; + + this.func_Chr = function(charCode) { + return String.fromCharCode(charCode); + }; + + this.sub_Cls = function() { + // TODO: parameter variants + var ctx = GX.ctx(); + ctx.beginPath(); + ctx.fillStyle = _bgColor.rgba(); + ctx.fillRect(0, 0, QB.func__Width() , QB.func__Height()); + }; + + function _color(c) { + if (c != undefined && c.r != undefined) { + return c; + } + return QB.func__RGB(0,0,0); + } + + this.sub_Color = function(fg, bg) { + if (fg != undefined) { + _fgColor = _color(fg); + } + if (bg != undefined) { + _bgColor = _color(bg); + } + }; + + this.func_Command = function() { + return ""; + }; + + this.func_Cos = function(value) { + return Math.cos(value); + }; + + this.func_Exp = function(value) { + return Math.exp(value); + }; + + this.func_Fix = function(value) { + if (value >=0) { + return Math.floor(value); + } + else { + return Math.floor(Math.abs(value)) * -1; + } + }; + + function _textColumns() { + return Math.floor(QB.func__Width() / QB.func__FontWidth()); + } + + function _textRows() { + return Math.floor(QB.func__Height() / QB.func__FontHeight()); + } + + this.sub_Input = async function(values, preventNewline, addQuestionPrompt, prompt) { + _lastKey = null; + var str = ""; + _inputMode = true; + + if (prompt != undefined) { + QB.sub_Print([prompt, QB.PREVENT_NEWLINE]); + } + if (prompt == undefined || addQuestionPrompt) { + QB.sub_Print(["? ", QB.PREVENT_NEWLINE]); + } + + if (!preventNewline && _locY > _textRows()-1) { + await _printScroll(); + _locY = _textRows()-1; + } + //QB.sub__PrintString(_locX * QB.func__FontWidth(), _locY * QB.func__FontHeight(), "? "); + //_locX += 2; + while (_lastKey != "Enter") { + + if (_lastKey == "Backspace" && str.length > 0) { + _locX--; + + var ctx = GX.ctx(); + ctx.beginPath(); + ctx.fillStyle = _bgColor.rgba(); + ctx.fillRect(_locX * QB.func__FontWidth(), _locY * QB.func__FontHeight(), QB.func__FontWidth() , QB.func__FontHeight()); + str = str.substring(0, str.length-1); + } + + else if (_lastKey && _lastKey.length < 2) { + QB.sub__PrintString(_locX * QB.func__FontWidth(), _locY * QB.func__FontHeight(), _lastKey); + _locX++; + str += _lastKey; + } + + _lastKey = null; + await GX.sleep(10); + } + if (!preventNewline) { + _locY++; + _locX = 0; + } + + if (values.length < 2) { + values[0] = str; + } + else { + var vparts = str.split(","); + for (var i=0; i < values.length; i++) { + values[i] = vparts[i] ? vparts[i] : ""; + } + } + _inputMode = false; + } + + this.func_InKey = function() { + if (_keyBuffer.length < 1) { + return ""; + } + return _keyBuffer.shift(); +// var temp = _lastKey; +// _lastKey = ""; +// return temp; + } + + this.func_InStr = function(arg1, arg2, arg3) { + var startIndex = 0; + var strSource = ""; + var strSearch = ""; + if (arg3 != undefined) { + startIndex = arg1-1; + strSource = String(arg2); + strSearch = String(arg3); + } + else { + strSource = String(arg1); + strSearch = String(arg2); + } + return strSource.indexOf(strSearch, startIndex)+1; + }; + + this.func_Int = function(value) { + return Math.floor(value); + }; + + this.func_LCase = function(value) { + return String(value).toLowerCase(); + }; + + this.func_Left = function(value, n) { + return String(value).substring(0, n); + }; + + this.func_Len = function(value) { + return String(value).length; + }; + + this.func_Log = function(value) { + return Math.log(value); + }; + + this.sub_Circle = function(step, x, y, radius, color, startAngle, endAngle, aspect) { + // TODO: implement aspect parameter + if (color == undefined) { + color = _fgColor; + } + else { + color = _color(color); + } + + if (startAngle == undefined) { startAngle = 0; } + if (endAngle == undefined) { endAngle = 2 * Math.PI; } + + if (step) { + x = _lastX + x; + y = _lastY + y; + } + _lastX = x; + _lastY = y; + + var ctx = GX.ctx(); + ctx.strokeStyle = color.rgba(); + ctx.beginPath(); + ctx.arc(x, y, radius, startAngle, endAngle); + ctx.stroke(); + }; + + this.sub_Line = function(sstep, sx, sy, estep, ex, ey, color, style, pattern) { + if (color == undefined) { + if (style == "BF") { + color = _bgColor; + } + else { + color = _fgColor; + } + } + else { + color = _color(color); + } + + if (sstep) { + sx = _lastX + sx; + sy = _lastY + sy; + } + if (sx == undefined) { + sx = _lastX; + sy = _lastY; + } + _lastX = sx; + _lastY = sy; + + if (estep) { + ex = _lastX + ex; + ey = _lastY + ey; + } + _lastX = ex; + _lastY = ey; + + var ctx = GX.ctx(); + + if (style == "B") { + ctx.strokeStyle = color.rgba(); + ctx.beginPath(); + ctx.strokeRect(sx, sy, ex-sx, ey-sy) + } + else if (style == "BF") { + ctx.fillStyle = color.rgba(); + ctx.beginPath(); + ctx.fillRect(sx, sy, ex-sx, ey-sy) + } + else { + ctx.strokeStyle = color.rgba(); + ctx.beginPath(); + ctx.moveTo(sx, sy); + ctx.lineTo(ex, ey); + ctx.stroke(); + } + }; + + this.sub_LineInput = async function(values, preventNewline, addQuestionPrompt, prompt) { + await QB.sub_Input(values, preventNewline, addQuestionPrompt, prompt); + } + + this.sub_Locate = function(row, col) { + // TODO: implement cursor positioning/display + if (row && row > 0 && row <= _textRows()) { + _locY = row-1; + } + if (col && col > 0 && col <= _textColumns()) { + _locX = col-1; + } + }; + + this.func_LTrim = function(value) { + return String(value).trimStart(); + } + + this.func_Mid = function(value, n, len) { + if (len == undefined) { + return String(value).substring(n-1); + } + else { + return String(value).substring(n-1, n+len-1); + } + }; + + this.sub_Print = async function(args) { + // Print called with no arguments + if (args == undefined || args == null || args.length < 1) { + args = [""]; + } + + var ctx = GX.ctx(); + var preventNewline = (args[args.length-1] == QB.PREVENT_NEWLINE || args[args.length-1] == QB.COLUMN_ADVANCE); + + for (var ai = 0; ai < args.length; ai++) { + if (args[ai] == QB.PREVENT_NEWLINE) { + // ignore as we will just concatenate the next arg + } + else if (args[ai] == QB.COLUMN_ADVANCE) { + // TODO: advance to the next column offset + _locX += 14 - _locX % 13; + } + else { + var str = args[ai]; + var lines = String(str).split("\n"); + for (var i=0; i < lines.length; i++) { + var x = _locX * QB.func__FontWidth(); + var y = -1; + + // scroll the screen + if (_locY < _textRows()-1) { + y = _locY * QB.func__FontHeight(); + //_locY = _locY + 1; + } + else { + //await _printScroll(); + + y = (_locY) * QB.func__FontHeight(); + } + + // TODO: check the background opacity mode + // Draw the text background + ctx.beginPath(); + ctx.fillStyle = _bgColor.rgba(); + ctx.fillRect(x, y, QB.func__FontWidth() * lines[i].length, QB.func__FontHeight()); + + //GX.drawText(_fntDefault, x, y, lines[i]); + ctx.font = "16px dosvga"; + ctx.fillStyle = _fgColor.rgba(); + ctx.fillText(lines[i], x, y+QB.func__FontHeight()-6); + + _locX += lines[i].length; + + if (i < lines.length-1) { + if (_locY < _textRows()-1) { + _locY = _locY + 1; + _locX = 0; + } + else { + await _printScroll(); + } + } + } + } + } + + if (!preventNewline) { + _locX = 0; + if (_locY < _textRows()-1) { + _locY = _locY + 1; + } + else { + await _printScroll(); + } + } + }; + +/* + this.sub_Print = async function(str) { + if (str == undefined || str == null) { + str = ""; + } + var ctx = GX.ctx(); + var lines = String(str).split("\n"); + for (var i=0; i < lines.length; i++) { + var x = _locX * QB.func__FontWidth(); + var y = -1; + + // scroll the screen + if (_locY < _textRows()) { + y = _locY * QB.func__FontHeight(); + _locY = _locY + 1; + } + else { + await _printScroll(); + + y = (_locY-1) * QB.func__FontHeight(); + } + + // TODO: check the background opacity mode + // Draw the text background + ctx.beginPath(); + ctx.fillStyle = _bgColor.rgba(); + ctx.fillRect(x, y, QB.func__FontWidth() * lines[0].length, QB.func__FontHeight()); + + GX.drawText(_fntDefault, x, y, lines[i]); + } + _locX = 0; + }; +*/ + async function _printScroll() { + var img = new Image(); + img.src = GX.canvas().toDataURL("image/png"); + while (!img.complete) { + await GX.sleep(10); + } + var ctx = GX.ctx(); + ctx.beginPath(); + ctx.fillStyle = _bgColor.rgba(); + ctx.fillRect(0, 0, QB.func__Width(), QB.func__Height()); + ctx.drawImage(img, 0, -QB.func__FontHeight()); + } + + this.sub_PSet = function(sstep, x, y, color) { + if (color == undefined) { + color = _fgColor; + } + else { + color = _color(color); + } + if (sstep) { + x = _lastX + x; + y = _lastY + y; + } + _lastX = x; + _lastY = y; + + var ctx = GX.ctx(); + ctx.fillStyle = color.rgba(); + ctx.beginPath(); + ctx.fillRect(x, y, 1, 1); + }; + + this.func_Right = function(value, n) { + if (value == undefined) { + return ""; + } + var s = String(value); + return s.substring(s.length-n, s.length); + }; + + this.func_RTrim = function(value) { + return String(value).trimEnd(); + } + + this.func_Rnd = function(n) { + // TODO: implement modifier parameter + return Math.random(); + } + + this.sub_Screen = async function(mode) { + if (mode == 0) { + GX.sceneCreate(640, 400); + //GX.fontLineSpacing(_fntDefault, 2); + } + else if (mode < 2 || mode == 7 || mode == 13) { + GX.sceneCreate(320, 200); + //GX.fontLineSpacing(_fntDefault, 0); + } + else if (mode == 8) { + GX.sceneCreate(640, 200); + //GX.fontLineSpacing(_fntDefault, 0); + } + else if (mode == 9 || mode == 10) { + GX.sceneCreate(640, 350); + //GX.fontLineSpacing(_fntDefault, 0); + } + else if (mode == 11 || mode == 12) { + GX.sceneCreate(640, 480); + //GX.fontLineSpacing(_fntDefault, 0); + } + else if (mode.width != undefined) { + GX.sceneCreate(mode.width, mode.height); + //GX.fontLineSpacing(_fntDefault, 2); + } + + // initialize the graphics + _fgColor = this.func__RGB(255, 255, 255); + _bgColor = this.func__RGB(0, 0, 0); + _lastX = 0; + _lastY = 0; + _locX = 0; + _locY = 0; + _lastKey = null; + _inputMode = false; + }; + + this.func_Sgn = function(value) { + if (value > 0) { return 1; } + else if (value < 0) { return -1; } + else { return 0; } + }; + + this.func_Sin = function(value) { + return Math.sin(value); + }; + + this.sub_Sleep = async function(seconds) { + var elapsed = 0; + var totalWait = Infinity; + if (seconds != undefined) { + totalWait = seconds*1000; + } + + _lastKey = null; + while (!_lastKey && elapsed < totalWait) { + await GX.sleep(100); + elapsed += 100; + } + }; + + this.func_Sqr = function(value) { + return Math.sqrt(value); + }; + + this.func_Str = function(value) { + return String(value); + }; + + this.sub_Swap = function(values) { + var temp = values[1]; + values[1] = values[0]; + values[0] = temp; + }; + + this.func_Tan = function(value) { + return Math.tan(value); + }; + + this.func_Timer = function(accuracy) { + // TODO: implement optional accuracy + var midnight = new Date(); + midnight.setHours(0, 0, 0, 0); + return ((new Date()).getTime() - midnight.getTime()) / 1000; + }; + + this.func_Atn = function(value) { + return Math.atan(value); + }; + + this.func_UBound = function(a, dimension) { + if (dimension == undefined) { + dimension = 1; + } + return a._dimensions[dimension-1]; + //return a.length-1; + }; + + this.func_UCase = function(value) { + return String(value).toUpperCase(); + }; + + this.func_Val = function(value) { + return Number(value); + }; + + + // QBJS-only methods + // --------------------------------------------------------------------------------- + this.func_Fetch = async function(url) { + var response = await fetch(url); + var responseText = await(response.text()); + return { + ok: response.ok, + status: response.status, + statusText: response.statusText, + text: responseText + }; + }; + + this.func_FromJSON = function(s) { + return JSON.parse(s); + }; + + this.func_ToJSON = function(a) { + return JSON.stringify(a); + } + + + function _init() { + // initialize the fonts + /*if (!_fntDefault) { + _fntDefault = GX.fontCreate("./qb/font.png", 8, 14, + "`1234567890-=~!@#$%^&*()_+\n" + + "qwertyuiop[]\\QWERTYUIOP{}|\n" + + "asdfghjkl;'ASDFGHJKL:\"\n" + + "zxcvbnm,./ZXCVBNM<>?"); + }*/ + + addEventListener("keydown", function(event) { + if (_inputMode) { + event.preventDefault(); + } + _lastKey = event.key; + }); + + addEventListener("keyup", function(event) { + _keyBuffer.push(event.key); + }); + + }; + + _init(); +} diff --git a/qb2js.js b/qb2js.js new file mode 100644 index 0000000..4759d9d --- /dev/null +++ b/qb2js.js @@ -0,0 +1,2134 @@ +var QBCompiler = new function() { + + // Option _Explicit + // $Console + // Only + const FILE = 1; + const TEXT = 2; + const False = 0; + const True = ! False; + + + + + + var lines = QB.initArray([ 0], {line:0,text:''}); // CODELINE + var jsLines = QB.initArray([ 0], {line:0,text:''}); // CODELINE + var methods = QB.initArray([ 0], {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}); // METHOD + var types = QB.initArray([ 0], {line:0,name:'',argc:0,args:''}); // QBTYPE + var typeVars = QB.initArray([ 0], {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}); // VARIABLE + var globalVars = QB.initArray([ 0], {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}); // VARIABLE + var localVars = QB.initArray([ 0], {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}); // VARIABLE + var warnings = QB.initArray([ 0], {line:0,text:''}); // CODELINE + var currentMethod = ''; // STRING + var programMethods = 0; // INTEGER + if ( QB.func_Command() != "" ) { + sub_QBToJS( QB.func_Command(), FILE); + sub_PrintJS(); + QB.halt(); return; + } + +async function sub_QBToJS(source/*STRING*/,sourceType/*INTEGER*/) { +if (QB.halted()) { return; } + QB.resizeArray(lines, [ 0], {line:0,text:''}, false); // CODELINE + QB.resizeArray(jsLines, [ 0], {line:0,text:''}, false); // CODELINE + QB.resizeArray(methods, [ 0], {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}, false); // METHOD + QB.resizeArray(types, [ 0], {line:0,name:'',argc:0,args:''}, false); // QBTYPE + QB.resizeArray(typeVars, [ 0], {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}, false); // VARIABLE + QB.resizeArray(globalVars, [ 0], {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}, false); // VARIABLE + QB.resizeArray(localVars, [ 0], {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}, false); // VARIABLE + QB.resizeArray(warnings, [ 0], {line:0,text:''}, false); // CODELINE + currentMethod = ""; + programMethods = 0; + if ( sourceType == FILE) { + sub_ReadLinesFromFile( source); + } else { + sub_ReadLinesFromText( source); + } + sub_FindMethods(); + programMethods = QB.func_UBound( methods); + sub_InitGX(); + sub_InitQBMethods(); + var selfConvert = 0; // INTEGER + var isGX = 0; // INTEGER + isGX = False; + if ( sourceType == FILE) { + selfConvert = func_EndsWith( source, "qb2js.bas"); + } + if ( selfConvert) { + sub_AddJSLine( 0, "var QBCompiler = new function() {"); + } else if ( sourceType == FILE) { + sub_AddJSLine( 0, "async function init() {"); + } + if (! selfConvert) { + sub_AddJSLine( 0, "QB.start();"); + } + if (! selfConvert) { + var mtest = {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}; // METHOD + if (func_FindMethod("GXOnGameEvent" , mtest, "SUB") ) { + sub_AddJSLine( 0, " await GX.registerGameEvents(sub_GXOnGameEvent);"); + isGX = True; + } else { + sub_AddJSLine( 0, " await GX.registerGameEvents(function(e){});"); + sub_AddJSLine( 0, " QB.sub_Screen(0);"); + } + } + sub_AddJSLine( 0, ""); + sub_ConvertLines( 1, func_MainEnd(), ""); + if (! selfConvert && ! isGX) { + sub_AddJSLine( 0, "QB.end();"); + } + sub_ConvertMethods(); + if ( selfConvert) { + sub_AddJSLine( 0, "this.compile = function(src) {"); + sub_AddJSLine( 0, " sub_QBToJS(src, TEXT);"); + sub_AddJSLine( 0, " var js = '';"); + sub_AddJSLine( 0, " for (var i=1; i<= QB.func_UBound(jsLines); i++) {"); + sub_AddJSLine( 0, " js += QB.arrayValue(jsLines, [i]).value.text + '\\n';"); + sub_AddJSLine( 0, " }"); + sub_AddJSLine( 0, " return js;"); + sub_AddJSLine( 0, "};"); + sub_AddJSLine( 0, "this.getWarnings = function() {"); + sub_AddJSLine( 0, " var w = [];"); + sub_AddJSLine( 0, " for (var i=1; i <= QB.func_UBound(warnings); i++) {"); + sub_AddJSLine( 0, " w.push({"); + sub_AddJSLine( 0, " line: QB.arrayValue(warnings, [i]).value.line,"); + sub_AddJSLine( 0, " text: QB.arrayValue(warnings, [i]).value.text"); + sub_AddJSLine( 0, " });"); + sub_AddJSLine( 0, " }"); + sub_AddJSLine( 0, " return w;"); + sub_AddJSLine( 0, "};"); + sub_AddJSLine( 0, "};"); + } else if ( sourceType == FILE) { + sub_AddJSLine( 0, "};"); + } +} +async function sub_PrintJS() { +if (QB.halted()) { return; } + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( jsLines); i= i + 1) { if (QB.halted()) { return; } + await QB.sub_Print([QB.arrayValue(jsLines, [ i]).value .text]); + } +} +async function sub_ConvertLines(firstLine/*INTEGER*/,lastLine/*INTEGER*/,functionName/*STRING*/) { +if (QB.halted()) { return; } + var typeMode = 0; // INTEGER + typeMode = False; + var jsMode = 0; // INTEGER + jsMode = False; + var i = 0; // INTEGER + var indent = 0; // INTEGER + var tempIndent = 0; // INTEGER + var m = {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}; // METHOD + var totalIndent = 0; // INTEGER + totalIndent = 1; + var caseCount = 0; // INTEGER + var loopMode = QB.initArray([ 100], 0); // INTEGER + var loopLevel = 0; // INTEGER + var caseVar = ''; // STRING + var currType = 0; // INTEGER + for ( i= firstLine; i <= lastLine; i= i + 1) { if (QB.halted()) { return; } + indent = 0; + tempIndent = 0; + var l = ''; // STRING + l = QB.func__Trim(QB.arrayValue(lines, [ i]).value .text); + var parts = QB.initArray([ 0], ''); // STRING + var c = 0; // INTEGER + c = func_SLSplit( l, parts); + var js = ''; // STRING + js = ""; + var first = ''; // STRING + first = QB.func_UCase(QB.arrayValue(parts, [ 1]).value); + if ( jsMode == True) { + if ( first == "$END" ) { + jsMode = False; + sub_AddJSLine( 0, "//-------- END JS native code block --------"); + } else { + sub_AddJSLine( i, QB.arrayValue(lines, [ i]).value .text); + } + } else if ( typeMode == True) { + if ( first == "END" ) { + var second = ''; // STRING + second = QB.func_UCase(QB.arrayValue(parts, [ 2]).value); + if ( second == "TYPE" ) { + typeMode = False; + } + } else { + var tvar = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + tvar.typeId = currType; + tvar.name = QB.arrayValue(parts, [ 1]).value; + tvar.type = QB.func_UCase(QB.arrayValue(parts, [ 3]).value); + if ( tvar.type == "_UNSIGNED" ) { + tvar.type = tvar.type +" " +QB.func_UCase(QB.arrayValue(parts, [ 4]).value); + } + sub_AddVariable( tvar, typeVars); + } + } else { + if ( first == "CONST" ) { + js = "const " +QB.arrayValue(parts, [ 2]).value +" = " +func_ConvertExpression(func_Join(parts , 4, -1, " ")) +";"; + sub_AddConst(QB.arrayValue(parts, [ 2]).value); + } else if ( first == "DIM" || first == "REDIM" || first == "STATIC" ) { + js = func_DeclareVar(parts); + } else if ( first == "SELECT" ) { + caseVar = func_GenJSVar(); + js = "var " + caseVar +" = " +func_ConvertExpression(func_Join(parts , 3, -1, " ")) +";" + GX.CRLF; + js = js +"switch (" + caseVar +") {"; + indent = 1; + caseCount = 0; + } else if ( first == "CASE" ) { + if ( caseCount > 0) { + js = "break;" + GX.LF; + } + if (QB.func_UCase(QB.arrayValue(parts, [ 2]).value) == "ELSE" ) { + js = js +"default:"; + } else if (QB.func_UCase(QB.arrayValue(parts, [ 2]).value) == "IS" ) { + js = js +"case " + caseVar +" " +func_ConvertExpression(func_Join(parts , 3, -1, " ")) +":"; + } else { + var caseParts = QB.initArray([ 0], ''); // STRING + var cscount = 0; // INTEGER + cscount = func_ListSplit(func_Join(parts , 2, -1, " ") , caseParts); + var ci = 0; // INTEGER + for ( ci= 1; ci <= cscount; ci= ci + 1) { if (QB.halted()) { return; } + if ( ci > 1) { + js = js + GX.CRLF; + } + js = js +"case " +func_ConvertExpression(QB.arrayValue(caseParts, [ ci]).value) +":"; + } + } + caseCount = caseCount + 1; + } else if ( first == "FOR" ) { + var fstep = ''; // STRING + fstep = "1"; + var eqIdx = 0; // INTEGER + var toIdx = 0; // INTEGER + var stepIdx = 0; // INTEGER + var fcond = ''; // STRING + fcond = " <= "; + stepIdx = 0; + var fi = 0; // INTEGER + for ( fi= 2; fi <= QB.func_UBound( parts); fi= fi + 1) { if (QB.halted()) { return; } + var fword = ''; // STRING + fword = QB.func_UCase(QB.arrayValue(parts, [ fi]).value); + if ( fword == "=" ) { + eqIdx = fi; + } else if ( fword == "TO" ) { + toIdx = fi; + } else if ( fword == "STEP" ) { + stepIdx = fi; + fstep = func_ConvertExpression(func_Join(parts , fi + 1, -1, " ")); + } + } + var fvar = ''; // STRING + fvar = func_ConvertExpression(func_Join(parts , 2, eqIdx - 1, " ")); + var sval = ''; // STRING + sval = func_ConvertExpression(func_Join(parts , eqIdx + 1, toIdx - 1, " ")); + var uval = ''; // STRING + uval = func_ConvertExpression(func_Join(parts , toIdx + 1, stepIdx - 1, " ")); + if (QB.func_Left(QB.func__Trim( fstep) , 1) == "-" ) { + fcond = " >= "; + } + js = "for (" + fvar +"=" + sval +"; " + fvar + fcond + uval +"; " + fvar +"=" + fvar +" + " + fstep +") {"; + js = js +" if (QB.halted()) { return; }"; + indent = 1; + } else if ( first == "IF" ) { + var thenIndex = 0; // INTEGER + for ( thenIndex= 2; thenIndex <= QB.func_UBound( parts); thenIndex= thenIndex + 1) { if (QB.halted()) { return; } + if (QB.func_UCase(QB.arrayValue(parts, [ thenIndex]).value) == "THEN" ) { + break; + } + } + js = "if (" +func_ConvertExpression(func_Join(parts , 2, thenIndex - 1, " ")) +") {"; + indent = 1; + } else if ( first == "ELSEIF" ) { + js = "} else if (" +func_ConvertExpression(func_Join(parts , 2, QB.func_UBound( parts) - 1, " ")) +") {"; + tempIndent = -1; + } else if ( first == "ELSE" ) { + js = "} else {"; + tempIndent = -1; + } else if ( first == "NEXT" ) { + js = js +"}"; + indent = -1; + } else if ( first == "END" ) { + if (QB.func_UBound( parts) == 1) { + js = "QB.halt(); return;"; + } else { + if (QB.func_UCase(QB.arrayValue(parts, [ 2]).value) == "SELECT" ) { + js = "break;"; + } + js = js +"}"; + indent = -1; + } + } else if ( first == "SYSTEM" ) { + js = "QB.halt(); return;"; + } else if ( first == "$IF" ) { + if (QB.func_UBound( parts) == 2) { + if (QB.func_UCase(QB.arrayValue(parts, [ 2]).value) == "JS" || QB.func_UCase(QB.arrayValue(parts, [ 2]).value) == "JAVASCRIPT" ) { + jsMode = True; + js = "//-------- BEGIN JS native code block --------"; + } + } + } else if ( first == "DO" ) { + loopLevel = loopLevel + 1; + if (QB.func_UBound( parts) > 1) { + if (QB.func_UCase(QB.arrayValue(parts, [ 2]).value) == "WHILE" ) { + js = "while (" +func_ConvertExpression(func_Join(parts , 3, -1, " ")) +") {"; + } else { + js = "while (!(" +func_ConvertExpression(func_Join(parts , 3, -1, " ")) +")) {"; + } + QB.arrayValue(loopMode, [ loopLevel]).value = 1; + } else { + js = "do {"; + QB.arrayValue(loopMode, [ loopLevel]).value = 2; + } + indent = 1; + js = js +" if (QB.halted()) { return; }"; + } else if ( first == "WHILE" ) { + loopLevel = loopLevel + 1; + js = "while (" +func_ConvertExpression(func_Join(parts , 2, -1, " ")) +") {"; + indent = 1; + js = js +" if (QB.halted()) { return; }"; + } else if ( first == "WEND" ) { + js = "}"; + loopLevel = loopLevel - 1; + indent = -1; + } else if ( first == "LOOP" ) { + if (QB.arrayValue(loopMode, [ loopLevel]).value == 1) { + js = "}"; + } else { + js = "} while (("; + if (QB.func_UBound( parts) < 2) { + js = js +"1));"; + } else { + if (QB.func_UCase(QB.arrayValue(parts, [ 2]).value) == "UNTIL" ) { + js = "} while (!("; + } + js = js +func_ConvertExpression(func_Join(parts , 3, QB.func_UBound( parts) , " ")) +"))"; + } + } + loopLevel = loopLevel - 1; + indent = -1; + } else if ( first == "_CONTINUE" ) { + js = "continue;"; + } else if (QB.func_UCase( l) == "EXIT FUNCTION" ) { + js = "return " + functionName +";"; + } else if (QB.func_UCase( l) == "EXIT SUB" ) { + js = "return;"; + } else if ( first == "EXIT" ) { + js = "break;"; + } else if ( first == "TYPE" ) { + typeMode = True; + var qbtype = {line:0,name:'',argc:0,args:''}; // QBTYPE + qbtype.line = i; + qbtype.name = QB.func_UCase(QB.arrayValue(parts, [ 2]).value); + sub_AddType( qbtype); + currType = QB.func_UBound( types); + } else if ( first == "CALL" ) { + var subline = ''; // STRING + subline = QB.func__Trim(func_Join(parts , 2, -1, " ")); + var subend = 0; // INTEGER + subend = QB.func_InStr( subline, "("); + var subname = ''; // STRING + if ( subend == 0) { + subname = subline; + } else { + subname = QB.func_Left( subline, subend - 1); + } + if (func_FindMethod( subname, m, "SUB") ) { + var subargs = ''; // STRING + subargs = QB.func_Mid( subline, QB.func_Len( subname) + 2, QB.func_Len( subline) -QB.func_Len( subname) - 2); + js = func_ConvertSub( m, subargs); + } else { + sub_AddWarning( i, "Missing Sub [" + subname +"], ignoring Call command"); + } + } else if ( c > 2) { + var assignment = 0; // INTEGER + assignment = 0; + var j = 0; // INTEGER + for ( j= 1; j <= QB.func_UBound( parts); j= j + 1) { if (QB.halted()) { return; } + if (QB.arrayValue(parts, [ j]).value == "=" ) { + assignment = j; + break; + } + } + if ( assignment > 0) { + js = func_RemoveSuffix(func_ConvertExpression(func_Join(parts , 1, assignment - 1, " "))) +" = " +func_ConvertExpression(func_Join(parts , assignment + 1, -1, " ")) +";"; + } else { + if (func_FindMethod(QB.arrayValue(parts, [ 1]).value , m, "SUB") ) { + js = func_ConvertSub( m, func_Join(parts , 2, -1, " ")); + } else { + js = "// " + l; + sub_AddWarning( i, "Missing/unsupported sub or syntax error"); + } + } + } else { + if (func_FindMethod(QB.arrayValue(parts, [ 1]).value , m, "SUB") ) { + js = func_ConvertSub( m, func_Join(parts , 2, -1, " ")); + } else { + js = "// " + l; + sub_AddWarning( i, "Missing/unsupported sub or syntax error"); + } + } + if (( indent < 0) ) { + totalIndent = totalIndent + indent; + } + sub_AddJSLine( i, GXSTR.lPad("" , " " , ( totalIndent + tempIndent) * 3) + js); + if (( indent > 0) ) { + totalIndent = totalIndent + indent; + } + } + } +} +function func_ConvertSub(m/*METHOD*/,args/*STRING*/) { +if (QB.halted()) { return; } +var ConvertSub = null; + var js = ''; // STRING + if ( m.name == "Line" ) { + var parts = QB.initArray([ 0], ''); // STRING + var plen = 0; // INTEGER + plen = func_SLSplit( args, parts); + if ( plen > 0) { + if (QB.func_UCase(QB.arrayValue(parts, [ 1]).value) == "INPUT" ) { + m.name = "Line Input"; + m.jsname = "await QB.sub_LineInput"; + args = func_Join(parts , 2, -1, " "); + } + } + } + if ( m.name == "Line" ) { + js = m.jsname +"(" +func_ConvertLine( args) +");"; + } else if ( m.name == "PSet" || m.name == "Circle" ) { + js = m.jsname +"(" +func_ConvertPSet( args) +");"; + } else if ( m.name == "_PrintString" ) { + js = m.jsname +"(" +func_ConvertPrintString( args) +");"; + } else if ( m.name == "Print" ) { + js = m.jsname +"(" +func_ConvertPrint( args) +");"; + } else if ( m.name == "Input" || m.name == "Line Input" ) { + js = func_ConvertInput( m, args); + } else if ( m.name == "Swap" ) { + js = func_ConvertSwap( m, args); + } else { + js = m.jsname +"(" +func_ConvertExpression( args) +");"; + } + ConvertSub = js; +return ConvertSub; +} +function func_ConvertLine(args/*STRING*/) { +if (QB.halted()) { return; } +var ConvertLine = null; + var firstParam = ''; // STRING + var theRest = ''; // STRING + var idx = 0; // INTEGER + var sstep = ''; // STRING + var estep = ''; // STRING + sstep = "false"; + estep = "false"; + idx = func_FindParamChar( args, ","); + if ( idx == -1) { + firstParam = args; + theRest = ""; + } else { + firstParam = QB.func_Left( args, idx - 1); + theRest = QB.func_Right( args, QB.func_Len( args) - idx); + } + idx = func_FindParamChar( firstParam, "-"); + var startCord = ''; // STRING + var endCord = ''; // STRING + if ( idx == -1) { + endCord = firstParam; + } else { + startCord = QB.func_Left( firstParam, idx - 1); + endCord = QB.func_Right( firstParam, QB.func_Len( firstParam) - idx); + } + if (QB.func_UCase(QB.func__Trim(QB.func_Left( startCord, 4))) == "STEP" ) { + sstep = "true"; + } + if (QB.func_UCase(QB.func__Trim(QB.func_Left( endCord, 4))) == "STEP" ) { + estep = "true"; + } + idx = QB.func_InStr( startCord, "("); + startCord = QB.func_Right( startCord, QB.func_Len( startCord) - idx); + idx = QB.func__InStrRev( startCord, ")"); + startCord = QB.func_Left( startCord, idx - 1); + startCord = func_ConvertExpression( startCord); + if ((QB.func__Trim( startCord) == "") ) { + startCord = "undefined, undefined"; + } + idx = QB.func_InStr( endCord, "("); + endCord = QB.func_Right( endCord, QB.func_Len( endCord) - idx); + idx = QB.func__InStrRev( endCord, ")"); + endCord = QB.func_Left( endCord, idx - 1); + endCord = func_ConvertExpression( endCord); + theRest = func_ConvertExpression( theRest); + theRest = GXSTR.replace( theRest, " BF" , " " +QB.func_Chr( 34) +"BF" +QB.func_Chr( 34)); + theRest = GXSTR.replace( theRest, " B" , " " +QB.func_Chr( 34) +"B" +QB.func_Chr( 34)); + ConvertLine = sstep +", " + startCord +", " + estep +", " + endCord +", " + theRest; +return ConvertLine; +} +function func_ConvertPSet(args/*STRING*/) { +if (QB.halted()) { return; } +var ConvertPSet = null; + var firstParam = ''; // STRING + var theRest = ''; // STRING + var idx = 0; // INTEGER + var sstep = ''; // STRING + sstep = "false"; + idx = func_FindParamChar( args, ","); + if ( idx == -1) { + firstParam = args; + theRest = ""; + } else { + firstParam = QB.func_Left( args, idx - 1); + theRest = QB.func_Right( args, QB.func_Len( args) - idx); + } + if (QB.func_UCase(QB.func__Trim(QB.func_Left( firstParam, 4))) == "STEP" ) { + sstep = "true"; + } + idx = QB.func_InStr( firstParam, "("); + firstParam = QB.func_Right( firstParam, QB.func_Len( firstParam) - idx); + idx = QB.func__InStrRev( firstParam, ")"); + firstParam = QB.func_Left( firstParam, idx - 1); + firstParam = func_ConvertExpression( firstParam); + if ((QB.func__Trim( firstParam) == "") ) { + firstParam = "undefined, undefined"; + } + theRest = func_ConvertExpression( theRest); + ConvertPSet = sstep +", " + firstParam +", " + theRest; +return ConvertPSet; +} +function func_ConvertPrint(args/*STRING*/) { +if (QB.halted()) { return; } +var ConvertPrint = null; + var pcount = 0; // INTEGER + var parts = QB.initArray([ 0], ''); // STRING + pcount = func_PrintSplit( args, parts); + var js = ''; // STRING + js = "["; + var i = 0; // INTEGER + for ( i= 1; i <= pcount; i= i + 1) { if (QB.halted()) { return; } + if ( i > 1) { + js = js +","; + } + if (QB.arrayValue(parts, [ i]).value == "," ) { + js = js +"QB.COLUMN_ADVANCE"; + } else if (QB.arrayValue(parts, [ i]).value == ";" ) { + js = js +"QB.PREVENT_NEWLINE"; + } else { + js = js +func_ConvertExpression(QB.arrayValue(parts, [ i]).value); + } + } + ConvertPrint = js +"]"; +return ConvertPrint; +} +function func_ConvertPrintString(args/*STRING*/) { +if (QB.halted()) { return; } +var ConvertPrintString = null; + var firstParam = ''; // STRING + var theRest = ''; // STRING + var idx = 0; // INTEGER + idx = func_FindParamChar( args, ","); + if ( idx == -1) { + firstParam = args; + theRest = ""; + } else { + firstParam = QB.func_Left( args, idx - 1); + theRest = QB.func_Right( args, QB.func_Len( args) - idx); + } + idx = QB.func_InStr( firstParam, "("); + firstParam = QB.func_Right( firstParam, QB.func_Len( firstParam) - idx); + idx = QB.func__InStrRev( firstParam, ")"); + firstParam = QB.func_Left( firstParam, idx - 1); + ConvertPrintString = func_ConvertExpression( firstParam) +", " +func_ConvertExpression( theRest); +return ConvertPrintString; +} +function func_ConvertInput(m/*METHOD*/,args/*STRING*/) { +if (QB.halted()) { return; } +var ConvertInput = null; + var js = ''; // STRING + var vname = ''; // STRING + var pcount = 0; // INTEGER + var parts = QB.initArray([ 0], ''); // STRING + var vars = QB.initArray([ 0], ''); // STRING + var varIndex = 0; // INTEGER + varIndex = 1; + var preventNewline = ''; // STRING + preventNewline = "false"; + var addQuestionPrompt = ''; // STRING + addQuestionPrompt = "false"; + var prompt = ''; // STRING + prompt = "undefined"; + var vcount = 0; // INTEGER + var p = ''; // STRING + pcount = func_PrintSplit( args, parts); + var i = 0; // INTEGER + for ( i= 1; i <= pcount; i= i + 1) { if (QB.halted()) { return; } + p = QB.func__Trim(QB.arrayValue(parts, [ i]).value); + if ( p == ";" ) { + if ( i == 1) { + preventNewline = "true"; + } else { + addQuestionPrompt = "true"; + } + } else if (func_StartsWith( p, QB.func_Chr( 34)) ) { + prompt = p; + } else if ( p != "," ) { + vcount = QB.func_UBound( vars) + 1; + QB.resizeArray(vars, [ vcount], '', true); // STRING + QB.arrayValue(vars, [ vcount]).value = p; + } + } + vname = func_GenJSVar(); + js = "var " + vname +" = new Array(" +QB.func_Str(QB.func_UBound( vars)) +");" + GX.LF; + js = js + m.jsname +"(" + vname +", " + preventNewline +", " + addQuestionPrompt +", " + prompt +");" + GX.LF; + for ( i= 1; i <= QB.func_UBound( vars); i= i + 1) { if (QB.halted()) { return; } + js = js +func_ConvertExpression(QB.arrayValue(vars, [ i]).value) +" = " + vname +"[" +QB.func_Str( i - 1) +"];" + GX.LF; + } + ConvertInput = js; +return ConvertInput; +} +function func_ConvertSwap(m/*METHOD*/,args/*STRING*/) { +if (QB.halted()) { return; } +var ConvertSwap = null; + var js = ''; // STRING + var swapArray = ''; // STRING + swapArray = func_GenJSVar(); + var swapArgs = QB.initArray([ 0], ''); // STRING + var swapCount = 0; // INTEGER + swapCount = func_ListSplit( args, swapArgs); + var var1 = ''; // STRING + var var2 = ''; // STRING + var1 = func_ConvertExpression(QB.arrayValue(swapArgs, [ 1]).value); + var2 = func_ConvertExpression(QB.arrayValue(swapArgs, [ 2]).value); + js = "var " + swapArray +" = [" + var1 +"," + var2 +"];" + GX.LF; + js = js + m.jsname +"(" + swapArray +");" + GX.LF; + js = js + var1 +" = " + swapArray +"[0];" + GX.LF; + js = js + var2 +" = " + swapArray +"[1];"; + ConvertSwap = js; +return ConvertSwap; +} +function func_GenJSVar() { +if (QB.halted()) { return; } +var GenJSVar = null; + GenJSVar = "___v" +QB.func__Trim(QB.func_Str(QB.func__Round( QB.func_Rnd() * 10000000))); +return GenJSVar; +} +function func_FindParamChar(s/*STRING*/,char/*STRING*/) { +if (QB.halted()) { return; } +var FindParamChar = null; + var idx = 0; // INTEGER + idx = -1; + var c = ''; // STRING + var quote = 0; // INTEGER + var paren = 0; // INTEGER + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_Len( s); i= i + 1) { if (QB.halted()) { return; } + c = QB.func_Mid( s, i, 1); + if ( c == QB.func_Chr( 34) ) { + quote = ! quote; + } else if (! quote && c == "(" ) { + paren = paren + 1; + } else if (! quote && c == ")" ) { + paren = paren - 1; + } else if (! quote && paren == 0 && c == char) { + idx = i; + break; + } + } + FindParamChar = idx; +return FindParamChar; +} +function func_DeclareVar(parts/*STRING*/) { +if (QB.halted()) { return; } +var DeclareVar = null; + var vname = ''; // STRING + var vtype = ''; // STRING + vtype = ""; + var vtypeIndex = 0; // INTEGER + vtypeIndex = 4; + var isGlobal = 0; // INTEGER + isGlobal = False; + var isArray = 0; // INTEGER + isArray = False; + var arraySize = ''; // STRING + var pstart = 0; // INTEGER + var bvar = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + var varnames = QB.initArray([ 0], ''); // STRING + var vnamecount = 0; // INTEGER + var findVar = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + var asIdx = 0; // INTEGER + asIdx = 0; + var js = ''; // STRING + js = ""; + var preserve = ''; // STRING + preserve = "false"; + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( parts); i= i + 1) { if (QB.halted()) { return; } + if (QB.func_UCase(QB.arrayValue(parts, [ i]).value) == "AS" ) { + asIdx = i; + } + if (QB.func_UCase(QB.arrayValue(parts, [ i]).value) == "_PRESERVE" ) { + preserve = "true"; + } + if (QB.func_UCase(QB.arrayValue(parts, [ i]).value) == "SHARED" ) { + isGlobal = True; + } + } + if ( asIdx == 2 || ( asIdx == 3 && ( isGlobal || preserve == "true")) || ( asIdx == 4 && isGlobal && preserve == "true") ) { + bvar.type = QB.func_UCase(QB.arrayValue(parts, [ asIdx + 1]).value); + var nextIdx = 0; // INTEGER + nextIdx = asIdx + 2; + if ( bvar.type == "_UNSIGNED" ) { + bvar.type = bvar.type +" " +QB.func_UCase(QB.arrayValue(parts, [ asIdx + 2]).value); + nextIdx = asIdx + 3; + } + bvar.typeId = func_FindTypeId( bvar.type); + vnamecount = func_ListSplit(func_Join(parts , nextIdx, -1, " ") , varnames); + for ( i= 1; i <= vnamecount; i= i + 1) { if (QB.halted()) { return; } + vname = QB.func__Trim(QB.arrayValue(varnames, [ i]).value); + pstart = QB.func_InStr( vname, "("); + if ( pstart > 0) { + bvar.isArray = True; + arraySize = func_ConvertExpression(QB.func_Mid( vname, pstart + 1, QB.func_Len( vname) - pstart - 1)); + bvar.name = func_RemoveSuffix(QB.func_Left( vname, pstart - 1)); + } else { + bvar.isArray = False; + arraySize = ""; + bvar.name = vname; + } + bvar.jsname = ""; + if (! bvar.isArray) { + js = js +"var " + bvar.name +" = " +func_InitTypeValue( bvar.type) +";"; + } else { + if (func_FindVariable( bvar.name, findVar, True) ) { + js = js +"QB.resizeArray(" + bvar.name +", [" + arraySize +"], " +func_InitTypeValue( bvar.type) +", " + preserve +");"; + } else { + js = js +"var " + bvar.name +" = QB.initArray([" + arraySize +"], " +func_InitTypeValue( bvar.type) +");"; + } + } + if ( isGlobal) { + sub_AddVariable( bvar, globalVars); + } else { + sub_AddVariable( bvar, localVars); + } + js = js +" // " + bvar.type; + if ( i < vnamecount) { + js = js + GX.LF; + } + } + } else { + var vpartcount = 0; // INTEGER + var vparts = QB.initArray([ 0], ''); // STRING + nextIdx = 0; + for ( i= 1; i <= QB.func_UBound( parts); i= i + 1) { if (QB.halted()) { return; } + var p = ''; // STRING + p = QB.func_UCase(QB.arrayValue(parts, [ i]).value); + if ( p == "DIM" || p == "REDIM" || p == "SHARED" || p == "_PRESERVE" ) { + nextIdx = i + 1; + } + } + vnamecount = func_ListSplit(func_Join(parts , nextIdx, -1, " ") , varnames); + for ( i= 1; i <= vnamecount; i= i + 1) { if (QB.halted()) { return; } + vpartcount = func_SLSplit(QB.arrayValue(varnames, [ i]).value , vparts); + bvar.name = func_RemoveSuffix(QB.arrayValue(vparts, [ 1]).value); + if ( vpartcount == 1) { + bvar.type = func_DataTypeFromName( bvar.name); + } else if ( vpartcount == 3) { + bvar.type = QB.func_UCase(QB.arrayValue(vparts, [ 3]).value); + } else if ( vpartcount == 4) { + bvar.type = QB.func_UCase(func_Join(vparts , 3, -1, " ")); + } else { + } + bvar.typeId = func_FindTypeId( bvar.type); + pstart = QB.func_InStr( bvar.name, "("); + if ( pstart > 0) { + bvar.isArray = True; + arraySize = func_ConvertExpression(QB.func_Mid( bvar.name, pstart + 1, QB.func_Len( bvar.name) - pstart - 1)); + bvar.name = func_RemoveSuffix(QB.func_Left( bvar.name, pstart - 1)); + } else { + bvar.isArray = False; + arraySize = ""; + } + bvar.jsname = ""; + if (! bvar.isArray) { + js = js +"var " + bvar.name +" = " +func_InitTypeValue( bvar.type) +";"; + } else { + if (func_FindVariable( bvar.name, findVar, True) ) { + js = js +"QB.resizeArray(" + bvar.name +", [" + arraySize +"], " +func_InitTypeValue( bvar.type) +", " + preserve +");"; + } else { + js = js +"var " + bvar.name +" = QB.initArray([" + arraySize +"], " +func_InitTypeValue( bvar.type) +");"; + } + } + if ( isGlobal) { + sub_AddVariable( bvar, globalVars); + } else { + sub_AddVariable( bvar, localVars); + } + js = js +" // " + bvar.type; + if ( i < vnamecount) { + js = js + GX.LF; + } + } + } + DeclareVar = js; +return DeclareVar; +} +function func_InitTypeValue(vtype/*STRING*/) { +if (QB.halted()) { return; } +var InitTypeValue = null; + var value = ''; // STRING + if ( vtype == "STRING" ) { + value = "''"; + } else if ( vtype == "_BIT" || vtype == "_UNSIGNED _BIT" || vtype == "_BYTE" || vtype == "_UNSIGNED _BYTE" || vtype == "INTEGER" || vtype == "_UNSIGNED INTEGER" || vtype == "LONG" || vtype == "_UNSIGNED LONG" || vtype == "_INTEGER64" || vtype == "_UNSIGNED INTEGER64" || vtype == "SINGLE" || vtype == "DOUBLE" || vtype == "_FLOAT" || vtype == "_OFFSET" || vtype == "_UNSIGNED _OFFSET" ) { + value = "0"; + } else { + value = "{"; + var typeId = 0; // INTEGER + typeId = func_FindTypeId( vtype); + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( typeVars); i= i + 1) { if (QB.halted()) { return; } + if ( typeId == QB.arrayValue(typeVars, [ i]).value .typeId) { + value = value +QB.arrayValue(typeVars, [ i]).value .name +":" +func_InitTypeValue(QB.arrayValue(typeVars, [ i]).value .type) +","; + } + } + value = QB.func_Left( value, QB.func_Len( value) - 1) +"}"; + } + InitTypeValue = value; +return InitTypeValue; +} +function func_FindTypeId(typeName/*STRING*/) { +if (QB.halted()) { return; } +var FindTypeId = null; + var id = 0; // INTEGER + id = -1; + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( types); i= i + 1) { if (QB.halted()) { return; } + if (QB.arrayValue(types, [ i]).value .name == typeName) { + id = i; + break; + } + } + FindTypeId = id; +return FindTypeId; +} +function func_ConvertExpression(ex/*STRING*/) { +if (QB.halted()) { return; } +var ConvertExpression = null; + var c = ''; // STRING + var js = ''; // STRING + js = ""; + var word = ''; // STRING + word = ""; + var bvar = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + var m = {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}; // METHOD + var stringLiteral = 0; // INTEGER + var i = 0; // INTEGER + i = 1; + while ( i <= QB.func_Len( ex)) { if (QB.halted()) { return; } + c = QB.func_Mid( ex, i, 1); + if ( c == QB.func_Chr( 34) ) { + js = js + c; + stringLiteral = ! stringLiteral; + } else if ( stringLiteral) { + js = js + c; + } else { + if ( c == " " || c == "," || i == QB.func_Len( ex) ) { + if ( i == QB.func_Len( ex) ) { + word = word + c; + } + var uword = ''; // STRING + uword = QB.func_UCase( word); + if ( uword == "NOT" ) { + js = js +"!"; + } else if ( uword == "AND" ) { + js = js +" && "; + } else if ( uword == "OR" ) { + js = js +" || "; + } else if ( uword == "MOD" ) { + js = js +" % "; + } else if ( word == "=" ) { + js = js +" == "; + } else if ( word == "<>" ) { + js = js +" != "; + } else if ( word == "^" ) { + js = js +" ** "; + } else if ( word == ">" || word == ">=" || word == "<" || word == "<=" ) { + js = js +" " + word +" "; + } else { + if (func_FindVariable( word, bvar, False) ) { + js = js +" " + bvar.jsname; + } else { + if (func_FindMethod( word, m, "FUNCTION") ) { + if ( m.name != currentMethod) { + js = js +" " + m.jsname +"()"; + } else { + js = js +" " + word; + } + } else { + js = js +" " + word; + } + } + } + if ( c == "," && i != QB.func_Len( ex) ) { + js = js +","; + } + word = ""; + } else if ( c == "(" ) { + var done = 0; // INTEGER + done = False; + var pcount = 0; // INTEGER + pcount = 0; + var c2 = ''; // STRING + var ex2 = ''; // STRING + ex2 = ""; + var stringLiteral2 = 0; // INTEGER + stringLiteral2 = False; + i = i + 1; + while (! done && i <= QB.func_Len( ex)) { if (QB.halted()) { return; } + c2 = QB.func_Mid( ex, i, 1); + if ( c2 == QB.func_Chr( 34) ) { + stringLiteral2 = ! stringLiteral2; + } else if (! stringLiteral2 && c2 == "(" ) { + pcount = pcount + 1; + } else if (! stringLiteral2 && c2 == ")" ) { + if ( pcount == 0) { + done = True; + } else { + pcount = pcount - 1; + } + } + if (! done) { + ex2 = ex2 + c2; + i = i + 1; + } + } + var fneg = ''; // STRING + fneg = ""; + if (QB.func_Len( word) > 0) { + if (QB.func_Left( word, 1) == "-" ) { + fneg = "-"; + word = QB.func_Mid( word, 2); + } + } + if (func_FindVariable( word, bvar, True) ) { + if (QB.func__Trim( ex2) == "" ) { + js = js + fneg + bvar.jsname; + } else { + js = js + fneg +"QB.arrayValue(" + bvar.jsname +", [" +func_ConvertExpression( ex2) +"]).value"; + } + } else if (func_FindMethod( word, m, "FUNCTION") ) { + js = js + fneg + m.jsname +"(" +func_ConvertExpression( ex2) +")"; + } else { + if (QB.func__Trim( word) != "" ) { + sub_AddWarning( i, "Missing function or array [" + word +"]"); + } + js = js + fneg +"(" +func_ConvertExpression( ex2) +")"; + } + word = ""; + } else { + word = word + c; + } + } + i = i + 1; + } + ConvertExpression = js; +return ConvertExpression; +} +function func_FindVariable(varname/*STRING*/,bvar/*VARIABLE*/,isArray/*INTEGER*/) { +if (QB.halted()) { return; } +var FindVariable = null; + var found = 0; // INTEGER + found = False; + var i = 0; // INTEGER + var fvarname = ''; // STRING + fvarname = QB.func__Trim(QB.func_UCase(func_RemoveSuffix( varname))); + for ( i= 1; i <= QB.func_UBound( localVars); i= i + 1) { if (QB.halted()) { return; } + if (QB.arrayValue(localVars, [ i]).value .isArray == isArray && QB.func_UCase(QB.arrayValue(localVars, [ i]).value .name) == fvarname) { + found = True; + bvar.type = QB.arrayValue(localVars, [ i]).value .type; + bvar.name = QB.arrayValue(localVars, [ i]).value .name; + bvar.jsname = QB.arrayValue(localVars, [ i]).value .jsname; + bvar.isConst = QB.arrayValue(localVars, [ i]).value .isConst; + bvar.isArray = QB.arrayValue(localVars, [ i]).value .isArray; + bvar.arraySize = QB.arrayValue(localVars, [ i]).value .arraySize; + bvar.typeId = QB.arrayValue(localVars, [ i]).value .typeId; + break; + } + } + if (! found) { + for ( i= 1; i <= QB.func_UBound( globalVars); i= i + 1) { if (QB.halted()) { return; } + if (QB.arrayValue(globalVars, [ i]).value .isArray == isArray && QB.func_UCase(QB.arrayValue(globalVars, [ i]).value .name) == fvarname) { + found = True; + bvar.type = QB.arrayValue(globalVars, [ i]).value .type; + bvar.name = QB.arrayValue(globalVars, [ i]).value .name; + bvar.jsname = QB.arrayValue(globalVars, [ i]).value .jsname; + bvar.isConst = QB.arrayValue(globalVars, [ i]).value .isConst; + bvar.isArray = QB.arrayValue(globalVars, [ i]).value .isArray; + bvar.arraySize = QB.arrayValue(globalVars, [ i]).value .arraySize; + bvar.typeId = QB.arrayValue(globalVars, [ i]).value .typeId; + break; + } + } + } + FindVariable = found; +return FindVariable; +} +function func_FindMethod(mname/*STRING*/,m/*METHOD*/,t/*STRING*/) { +if (QB.halted()) { return; } +var FindMethod = null; + var found = 0; // INTEGER + found = False; + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( methods); i= i + 1) { if (QB.halted()) { return; } + if (QB.arrayValue(methods, [ i]).value .uname == QB.func__Trim(QB.func_UCase(func_RemoveSuffix( mname))) && QB.arrayValue(methods, [ i]).value .type == t) { + found = True; + m.line = QB.arrayValue(methods, [ i]).value .line; + m.type = QB.arrayValue(methods, [ i]).value .type; + m.returnType = QB.arrayValue(methods, [ i]).value .returnType; + m.name = QB.arrayValue(methods, [ i]).value .name; + m.jsname = QB.arrayValue(methods, [ i]).value .jsname; + m.uname = QB.arrayValue(methods, [ i]).value .uname; + m.argc = QB.arrayValue(methods, [ i]).value .argc; + m.args = QB.arrayValue(methods, [ i]).value .args; + break; + } + } + FindMethod = found; +return FindMethod; +} +async function sub_ConvertMethods() { +if (QB.halted()) { return; } + sub_AddJSLine( 0, ""); + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( methods); i= i + 1) { if (QB.halted()) { return; } + if ((QB.arrayValue(methods, [ i]).value .line != 0) ) { + var lastLine = 0; // INTEGER + lastLine = QB.arrayValue(methods, [ i + 1]).value .line - 1; + if ( lastLine < 0) { + lastLine = QB.func_UBound( lines); + } + QB.resizeArray(localVars, [ 0], {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}, false); // VARIABLE + var asyncModifier = ''; // STRING + if (QB.arrayValue(methods, [ i]).value .type == "SUB" ) { + asyncModifier = "async "; + } else { + asyncModifier = ""; + } + var methodDec = ''; // STRING + methodDec = asyncModifier +"function " +QB.arrayValue(methods, [ i]).value .jsname +"("; + if (QB.arrayValue(methods, [ i]).value .argc > 0) { + var args = QB.initArray([ 0], ''); // STRING + var c = 0; // INTEGER + c = func_Split(QB.arrayValue(methods, [ i]).value .args, "," , args); + var a = 0; // INTEGER + for ( a= 1; a <= c; a= a + 1) { if (QB.halted()) { return; } + var v = 0; // INTEGER + var parts = QB.initArray([ 0], ''); // STRING + v = func_Split(QB.arrayValue(args, [ a]).value , ":" , parts); + methodDec = methodDec +QB.arrayValue(parts, [ 1]).value +"/*" +QB.arrayValue(parts, [ 2]).value +"*/"; + if ( a < c) { + methodDec = methodDec +","; + } + var bvar = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + bvar.name = QB.arrayValue(parts, [ 1]).value; + bvar.type = QB.arrayValue(parts, [ 2]).value; + bvar.typeId = func_FindTypeId( bvar.type); + if (QB.arrayValue(parts, [ 3]).value == "true" ) { + bvar.isArray = True; + } + bvar.jsname = ""; + sub_AddVariable( bvar, localVars); + } + } + methodDec = methodDec +") {"; + sub_AddJSLine(QB.arrayValue(methods, [ i]).value .line, methodDec); + sub_AddJSLine(QB.arrayValue(methods, [ i]).value .line, "if (QB.halted()) { return; }"); + if (QB.arrayValue(methods, [ i]).value .type == "FUNCTION" ) { + sub_AddJSLine(QB.arrayValue(methods, [ i]).value .line, "var " +func_RemoveSuffix(QB.arrayValue(methods, [ i]).value .name) +" = null;"); + } + currentMethod = QB.arrayValue(methods, [ i]).value .name; + sub_ConvertLines(QB.arrayValue(methods, [ i]).value .line + 1, lastLine - 1, QB.arrayValue(methods, [ i]).value .name); + if (QB.arrayValue(methods, [ i]).value .type == "FUNCTION" ) { + sub_AddJSLine( lastLine, "return " +func_RemoveSuffix(QB.arrayValue(methods, [ i]).value .name) +";"); + } + sub_AddJSLine( lastLine, "}"); + } + } +} +async function sub_ReadLinesFromFile(filename/*STRING*/) { +if (QB.halted()) { return; } + var fline = ''; // STRING + var lineIndex = 0; // INTEGER + // Open filename For Input As #1 + while (!(( 1))) { if (QB.halted()) { return; } + var ___v7055475 = new Array( 2); +await QB.sub_LineInput(___v7055475, false, false, undefined); +// #1 = ___v7055475[ 0]; + fline = ___v7055475[ 1]; + + lineIndex = lineIndex + 1; + if (QB.func__Trim( fline) != "" ) { + while (func_EndsWith( fline, " _")) { if (QB.halted()) { return; } + var nextLine = ''; // STRING + var ___v5334240 = new Array( 2); +await QB.sub_LineInput(___v5334240, false, false, undefined); +// #1 = ___v5334240[ 0]; + nextLine = ___v5334240[ 1]; + + fline = QB.func_Left( fline, QB.func_Len( fline) - 1) + nextLine; + } + sub_ReadLine( lineIndex, fline); + } + } + // Close #1 +} +async function sub_ReadLinesFromText(sourceText/*STRING*/) { +if (QB.halted()) { return; } + var sourceLines = QB.initArray([ 0], ''); // STRING + var lcount = 0; // INTEGER + var i = 0; // INTEGER + lcount = func_Split( sourceText, GX.LF, sourceLines); + for ( i= 1; i <= lcount; i= i + 1) { if (QB.halted()) { return; } + var fline = ''; // STRING + fline = QB.arrayValue(sourceLines, [ i]).value; + if (QB.func__Trim( fline) != "" ) { + var lineIndex = 0; // INTEGER + lineIndex = i; + while (func_EndsWith( fline, "_")) { if (QB.halted()) { return; } + i = i + 1; + var nextLine = ''; // STRING + nextLine = QB.arrayValue(sourceLines, [ i]).value; + fline = QB.func_Left( fline, QB.func_Len( fline) - 1) + nextLine; + } + sub_ReadLine( i, fline); + } + } +} +async function sub_ReadLine(lineIndex/*INTEGER*/,fline/*STRING*/) { +if (QB.halted()) { return; } + var quoteDepth = 0; // INTEGER + quoteDepth = 0; + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_Len( fline); i= i + 1) { if (QB.halted()) { return; } + var c = ''; // STRING + c = QB.func_Mid( fline, i, 1); + if ( c == QB.func_Chr( 34) ) { + if ( quoteDepth == 0) { + quoteDepth = 1; + } else { + quoteDepth = 0; + } + } + if ( quoteDepth == 0 && c == "'" ) { + fline = QB.func_Left( fline, i - 1); + break; + } + if ( quoteDepth == 0 && c == ":" ) { + sub_AddLine( lineIndex, QB.func_Left( fline, i - 1)); + fline = QB.func_Right( fline, QB.func_Len( fline) - i); + i = 0; + } + } + if (QB.func__Trim( fline) != "" ) { + sub_AddLine( lineIndex, fline); + } +} +async function sub_FindMethods() { +if (QB.halted()) { return; } + var i = 0; // INTEGER + var pcount = 0; // INTEGER + var parts = QB.initArray([ 0], ''); // STRING + for ( i= 1; i <= QB.func_UBound( lines); i= i + 1) { if (QB.halted()) { return; } + pcount = func_Split(QB.arrayValue(lines, [ i]).value .text, " " , parts); + var word = ''; // STRING + word = QB.func_UCase(QB.arrayValue(parts, [ 1]).value); + if ( word == "FUNCTION" || word == "SUB" ) { + var m = {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}; // METHOD + m.line = i; + m.type = QB.func_UCase(QB.arrayValue(parts, [ 1]).value); + m.name = QB.arrayValue(parts, [ 2]).value; + m.argc = 0; + m.args = ""; + var args = QB.initArray([ 0], {name:'',type:''}); // ARGUMENT + if (QB.func_UBound( parts) > 2) { + var a = 0; // INTEGER + var args = ''; // STRING + args = ""; + for ( a= 3; a <= QB.func_UBound( parts); a= a + 1) { if (QB.halted()) { return; } + args = args +QB.arrayValue(parts, [ a]).value +" "; + } + args = QB.func_Mid(QB.func__Trim( args) , 2, QB.func_Len(QB.func__Trim( args)) - 2); + var arga = QB.initArray([ 0], ''); // STRING + m.argc = func_ListSplit( args, arga); + args = ""; + for ( a= 1; a <= m.argc; a= a + 1) { if (QB.halted()) { return; } + var aparts = QB.initArray([ 0], ''); // STRING + var apcount = 0; // INTEGER + var argname = ''; // STRING + var isArray = ''; // STRING + isArray = "false"; + apcount = func_Split(QB.arrayValue(arga, [ a]).value , " " , aparts); + argname = QB.arrayValue(aparts, [ 1]).value; + if (func_EndsWith( argname, "()") ) { + isArray = "true"; + argname = QB.func_Left( argname, QB.func_Len( argname) - 2); + } + if ( apcount == 3) { + args = args + argname +":" +QB.func_UCase(QB.arrayValue(aparts, [ 3]).value) +":" + isArray; + } else { + args = args + argname +":" +func_DataTypeFromName(QB.arrayValue(aparts, [ 1]).value) +":" + isArray; + } + if ( a != m.argc) { + args = args +","; + } + } + m.args = args; + } + sub_AddMethod( m, ""); + } + } +} +function func_Split(sourceString/*STRING*/,delimiter/*STRING*/,results/*STRING*/) { +if (QB.halted()) { return; } +var Split = null; + var cstr = ''; // STRING + var p = 0; // LONG +var curpos = 0; // LONG +var arrpos = 0; // LONG +var dpos = 0; // LONG + cstr = sourceString; + if ( delimiter == " " ) { + cstr = QB.func_RTrim(QB.func_LTrim( cstr)); + p = QB.func_InStr( cstr, " "); + while ( p > 0) { if (QB.halted()) { return; } + cstr = QB.func_Mid( cstr, 1, p - 1) +QB.func_Mid( cstr, p + 1); + p = QB.func_InStr( cstr, " "); + } + } + curpos = 1; + arrpos = 0; + dpos = QB.func_InStr( curpos, cstr, delimiter); + while (!( dpos == 0)) { if (QB.halted()) { return; } + arrpos = arrpos + 1; + QB.resizeArray(results, [ arrpos], '', true); // STRING + QB.arrayValue(results, [ arrpos]).value = QB.func_Mid( cstr, curpos, dpos - curpos); + curpos = dpos +QB.func_Len( delimiter); + dpos = QB.func_InStr( curpos, cstr, delimiter); + } + arrpos = arrpos + 1; + QB.resizeArray(results, [ arrpos], '', true); // STRING + QB.arrayValue(results, [ arrpos]).value = QB.func_Mid( cstr, curpos); + Split = arrpos; +return Split; +} +function func_SLSplit(sourceString/*STRING*/,results/*STRING*/) { +if (QB.halted()) { return; } +var SLSplit = null; + var cstr = ''; // STRING + var p = 0; // LONG +var curpos = 0; // LONG +var arrpos = 0; // LONG +var dpos = 0; // LONG + cstr = QB.func__Trim( sourceString); + QB.resizeArray(results, [ 0], '', false); // STRING + var lastChar = ''; // STRING + var quoteMode = 0; // INTEGER + var result = ''; // STRING + var count = 0; // INTEGER + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_Len( cstr); i= i + 1) { if (QB.halted()) { return; } + var c = ''; // STRING + c = QB.func_Mid( cstr, i, 1); + if ( c == QB.func_Chr( 34) ) { + quoteMode = ! quoteMode; + result = result + c; + if (! quoteMode) { + result = GXSTR.replace( result, "\\" , "\\\\"); + } + } else if ( c == " " ) { + if ( quoteMode) { + result = result + c; + } else if ( lastChar == " " ) { + } else { + count = QB.func_UBound( results) + 1; + QB.resizeArray(results, [ count], '', true); // STRING + QB.arrayValue(results, [ count]).value = result; + result = ""; + } + } else { + result = result + c; + } + lastChar = c; + } + if ( result != "" ) { + count = QB.func_UBound( results) + 1; + QB.resizeArray(results, [ count], '', true); // STRING + QB.arrayValue(results, [ count]).value = result; + } + SLSplit = QB.func_UBound( results); +return SLSplit; +} +function func_ListSplit(sourceString/*STRING*/,results/*STRING*/) { +if (QB.halted()) { return; } +var ListSplit = null; + var cstr = ''; // STRING + var p = 0; // LONG +var curpos = 0; // LONG +var arrpos = 0; // LONG +var dpos = 0; // LONG + cstr = QB.func__Trim( sourceString); + QB.resizeArray(results, [ 0], '', false); // STRING + var quoteMode = 0; // INTEGER + var result = ''; // STRING + var count = 0; // INTEGER + var paren = 0; // INTEGER + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_Len( cstr); i= i + 1) { if (QB.halted()) { return; } + var c = ''; // STRING + c = QB.func_Mid( cstr, i, 1); + if ( c == QB.func_Chr( 34) ) { + quoteMode = ! quoteMode; + result = result + c; + } else if ( quoteMode) { + result = result + c; + } else if ( c == "(" ) { + paren = paren + 1; + result = result + c; + } else if ( c == ")" ) { + paren = paren - 1; + result = result + c; + } else if ( paren > 0) { + result = result + c; + } else if ( c == "," ) { + count = QB.func_UBound( results) + 1; + QB.resizeArray(results, [ count], '', true); // STRING + QB.arrayValue(results, [ count]).value = result; + result = ""; + } else { + result = result + c; + } + } + if ( result != "" ) { + count = QB.func_UBound( results) + 1; + QB.resizeArray(results, [ count], '', true); // STRING + QB.arrayValue(results, [ count]).value = result; + } + ListSplit = QB.func_UBound( results); +return ListSplit; +} +function func_PrintSplit(sourceString/*STRING*/,results/*STRING*/) { +if (QB.halted()) { return; } +var PrintSplit = null; + var cstr = ''; // STRING + var p = 0; // LONG +var curpos = 0; // LONG +var arrpos = 0; // LONG +var dpos = 0; // LONG + cstr = QB.func__Trim( sourceString); + QB.resizeArray(results, [ 0], '', false); // STRING + var quoteMode = 0; // INTEGER + var result = ''; // STRING + var count = 0; // INTEGER + var paren = 0; // INTEGER + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_Len( cstr); i= i + 1) { if (QB.halted()) { return; } + var c = ''; // STRING + c = QB.func_Mid( cstr, i, 1); + if ( c == QB.func_Chr( 34) ) { + quoteMode = ! quoteMode; + result = result + c; + } else if ( quoteMode) { + result = result + c; + } else if ( c == "(" ) { + paren = paren + 1; + result = result + c; + } else if ( c == ")" ) { + paren = paren - 1; + result = result + c; + } else if ( paren > 0) { + result = result + c; + } else if ( c == "," || c == ";" ) { + if ( result != "" ) { + count = QB.func_UBound( results) + 1; + QB.resizeArray(results, [ count], '', true); // STRING + QB.arrayValue(results, [ count]).value = result; + result = ""; + } + count = QB.func_UBound( results) + 1; + QB.resizeArray(results, [ count], '', true); // STRING + QB.arrayValue(results, [ count]).value = c; + } else { + result = result + c; + } + } + if ( result != "" ) { + count = QB.func_UBound( results) + 1; + QB.resizeArray(results, [ count], '', true); // STRING + QB.arrayValue(results, [ count]).value = result; + } + PrintSplit = QB.func_UBound( results); +return PrintSplit; +} +async function sub_PrintMethods() { +if (QB.halted()) { return; } + await QB.sub_Print([""]); + await QB.sub_Print(["Methods"]); + await QB.sub_Print(["------------------------------------------------------------"]); + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( methods); i= i + 1) { if (QB.halted()) { return; } + var m = {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}; // METHOD + m = QB.arrayValue(methods, [ i]).value; + await QB.sub_Print([QB.func_Str( m.line) +": " + m.type +" - " + m.name +" [" + m.jsname +"] - " + m.returnType +" - " + m.args]); + } +} +async function sub_PrintTypes() { +if (QB.halted()) { return; } + await QB.sub_Print([""]); + await QB.sub_Print(["Types"]); + await QB.sub_Print(["------------------------------------------------------------"]); + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( types); i= i + 1) { if (QB.halted()) { return; } + var t = {line:0,name:'',argc:0,args:''}; // QBTYPE + t = QB.arrayValue(types, [ i]).value; + await QB.sub_Print([QB.func_Str( t.line) +": " + t.name]); + var v = 0; // INTEGER + for ( v= 1; v <= QB.func_UBound( typeVars); v= v + 1) { if (QB.halted()) { return; } + if (QB.arrayValue(typeVars, [ i]).value .typeId == i) { + await QB.sub_Print([" -> " +QB.arrayValue(typeVars, [ v]).value .name +": " +QB.arrayValue(typeVars, [ v]).value .type]); + } + } + } +} +async function sub_AddMethod(m/*METHOD*/,prefix/*STRING*/) { +if (QB.halted()) { return; } + var mcount = 0; // SINGLE + mcount = QB.func_UBound( methods) + 1; + QB.resizeArray(methods, [ mcount], {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}, true); // METHOD + if ( m.type == "FUNCTION" ) { + m.returnType = func_DataTypeFromName( m.name); + } + m.uname = QB.func_UCase(func_RemoveSuffix( m.name)); + m.jsname = func_MethodJS( m, prefix); + QB.arrayValue(methods, [ mcount]).value = m; +} +async function sub_AddGXMethod(mtype/*STRING*/,mname/*STRING*/) { +if (QB.halted()) { return; } + var mcount = 0; // SINGLE + mcount = QB.func_UBound( methods) + 1; + QB.resizeArray(methods, [ mcount], {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}, true); // METHOD + var m = {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}; // METHOD + m.type = mtype; + m.name = mname; + m.uname = QB.func_UCase( m.name); + m.jsname = func_GXMethodJS(func_RemoveSuffix( mname)); + if ( mtype == "FUNCTION" ) { + m.returnType = func_DataTypeFromName( mname); + } + QB.arrayValue(methods, [ mcount]).value = m; +} +async function sub_AddQBMethod(mtype/*STRING*/,mname/*STRING*/) { +if (QB.halted()) { return; } + var m = {line:0,type:'',returnType:'',name:'',uname:'',argc:0,args:'',jsname:''}; // METHOD + m.type = mtype; + m.name = mname; + sub_AddMethod( m, "QB."); +} +async function sub_AddLine(lineIndex/*INTEGER*/,fline/*STRING*/) { +if (QB.halted()) { return; } + var parts = QB.initArray([ 0], ''); // STRING + var c = 0; // INTEGER + c = func_Split( fline, " " , parts); + if (QB.func_UCase(QB.arrayValue(parts, [ 1]).value) == "IF" ) { + var thenIndex = 0; // INTEGER + thenIndex = 0; + var i = 0; // INTEGER + for ( i= 1; i <= c; i= i + 1) { if (QB.halted()) { return; } + if (QB.func_UCase(QB.arrayValue(parts, [ i]).value) == "THEN" ) { + thenIndex = i; + break; + } + } + if ( thenIndex != c) { + sub___AddLine( lineIndex, func_Join(parts , 1, thenIndex, " ")); + sub___AddLine( lineIndex, func_Join(parts , thenIndex + 1, c, " ")); + sub___AddLine( lineIndex, "End If"); + } else { + sub___AddLine( lineIndex, fline); + } + } else { + sub___AddLine( lineIndex, fline); + } +} +async function sub___AddLine(lineIndex/*INTEGER*/,fline/*STRING*/) { +if (QB.halted()) { return; } + var lcount = 0; // INTEGER + lcount = QB.func_UBound( lines) + 1; + QB.resizeArray(lines, [ lcount], {line:0,text:''}, true); // CODELINE + QB.arrayValue(lines, [ lcount]).value .line = lineIndex; + QB.arrayValue(lines, [ lcount]).value .text = fline; +} +async function sub_AddJSLine(sourceLine/*INTEGER*/,jsline/*STRING*/) { +if (QB.halted()) { return; } + var lcount = 0; // INTEGER + lcount = QB.func_UBound( jsLines) + 1; + QB.resizeArray(jsLines, [ lcount], {line:0,text:''}, true); // CODELINE + QB.arrayValue(jsLines, [ lcount]).value .line = sourceLine; + QB.arrayValue(jsLines, [ lcount]).value .text = jsline; +} +async function sub_AddWarning(sourceLine/*INTEGER*/,msgText/*STRING*/) { +if (QB.halted()) { return; } + var lcount = 0; // INTEGER + lcount = QB.func_UBound( warnings) + 1; + QB.resizeArray(warnings, [ lcount], {line:0,text:''}, true); // CODELINE + var l = 0; // INTEGER + if (( sourceLine > 0) ) { + l = QB.arrayValue(lines, [ sourceLine]).value .line; + } + QB.arrayValue(warnings, [ lcount]).value .line = l; + QB.arrayValue(warnings, [ lcount]).value .text = msgText; +} +async function sub_AddConst(vname/*STRING*/) { +if (QB.halted()) { return; } + var v = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + v.type = "CONST"; + v.name = vname; + v.isConst = True; + sub_AddVariable( v, globalVars); +} +async function sub_AddGXConst(vname/*STRING*/) { +if (QB.halted()) { return; } + var v = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + v.type = "CONST"; + v.name = vname; + if ( vname == "GX_TRUE" ) { + v.jsname = "true"; + } else if ( vname == "GX_FALSE" ) { + v.jsname = "false"; + } else { + var jsname = ''; // STRING + jsname = QB.func_Mid( vname, 3, QB.func_Len( vname) - 2); + if (QB.func_Left( jsname, 1) == "_" ) { + jsname = QB.func_Right( jsname, QB.func_Len( jsname) - 1); + } + v.jsname = "GX." + jsname; + } + v.isConst = True; + sub_AddVariable( v, globalVars); +} +async function sub_AddGlobal(vname/*STRING*/,vtype/*STRING*/,arraySize/*INTEGER*/) { +if (QB.halted()) { return; } + var v = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + v.type = vtype; + v.name = vname; + v.isArray = arraySize > -1; + v.arraySize = arraySize; + sub_AddVariable( v, globalVars); +} +async function sub_AddLocal(vname/*STRING*/,vtype/*STRING*/,arraySize/*INTEGER*/) { +if (QB.halted()) { return; } + var v = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + v.type = vtype; + v.name = vname; + v.isArray = arraySize > -1; + v.arraySize = arraySize; + sub_AddVariable( v, localVars); +} +async function sub_AddVariable(bvar/*VARIABLE*/,vlist/*VARIABLE*/) { +if (QB.halted()) { return; } + var vcount = 0; // SINGLE + vcount = QB.func_UBound( vlist) + 1; + QB.resizeArray(vlist, [ vcount], {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}, true); // VARIABLE + if ( bvar.jsname == "" ) { + bvar.jsname = func_RemoveSuffix( bvar.name); + } + QB.arrayValue(vlist, [ vcount]).value = bvar; +} +async function sub_AddType(t/*QBTYPE*/) { +if (QB.halted()) { return; } + var tcount = 0; // SINGLE + tcount = QB.func_UBound( types) + 1; + QB.resizeArray(types, [ tcount], {line:0,name:'',argc:0,args:''}, true); // QBTYPE + QB.arrayValue(types, [ tcount]).value = t; +} +async function sub_AddSystemType(tname/*STRING*/,args/*STRING*/) { +if (QB.halted()) { return; } + var t = {line:0,name:'',argc:0,args:''}; // QBTYPE + t.name = tname; + sub_AddType( t); + var typeId = 0; // INTEGER + typeId = QB.func_UBound( types); + var count = 0; // INTEGER + var pairs = QB.initArray([ 0], ''); // STRING + count = func_Split( args, "," , pairs); + var i = 0; // INTEGER + for ( i= 1; i <= QB.func_UBound( pairs); i= i + 1) { if (QB.halted()) { return; } + var nv = QB.initArray([ 0], ''); // STRING + count = func_Split(QB.arrayValue(pairs, [ i]).value , ":" , nv); + var tvar = {type:'',name:'',jsname:'',isConst:0,isArray:0,arraySize:0,typeId:0}; // VARIABLE + tvar.typeId = typeId; + tvar.name = QB.arrayValue(nv, [ 1]).value; + tvar.type = QB.func_UCase(QB.arrayValue(nv, [ 2]).value); + sub_AddVariable( tvar, typeVars); + } +} +function func_MainEnd() { +if (QB.halted()) { return; } +var MainEnd = null; + if ( programMethods == 0) { + MainEnd = QB.func_UBound( lines); + } else { + MainEnd = QB.arrayValue(methods, [ 1]).value .line - 1; + } +return MainEnd; +} +function func_RemoveSuffix(vname/*STRING*/) { +if (QB.halted()) { return; } +var RemoveSuffix = null; + var i = 0; // INTEGER + var done = 0; // INTEGER + var c = ''; // STRING + vname = QB.func__Trim( vname); + i = QB.func_Len( vname); + while (! done) { if (QB.halted()) { return; } + c = QB.func_Mid( vname, i, 1); + if ( c == "`" || c == "%" || c == "&" || c == "$" || c == "~" || c == "!" ) { + i = i - 1; + } else { + done = True; + } + } + RemoveSuffix = QB.func_Left( vname, i); +return RemoveSuffix; +} +function func_DataTypeFromName(vname/*STRING*/) { +if (QB.halted()) { return; } +var DataTypeFromName = null; + var dt = ''; // STRING + if (func_EndsWith( vname, "$") ) { + dt = "STRING"; + } else if (func_EndsWith( vname, "`") ) { + dt = "_BIT"; + } else if (func_EndsWith( vname, "%%") ) { + dt = "_BYTE"; + } else if (func_EndsWith( vname, "~%") ) { + dt = "_UNSIGNED INTEGER"; + } else if (func_EndsWith( vname, "%") ) { + dt = "INTEGER"; + } else if (func_EndsWith( vname, "~&&") ) { + dt = "_UNSIGNED INTEGER64"; + } else if (func_EndsWith( vname, "&&") ) { + dt = "_INTEGER64"; + } else if (func_EndsWith( vname, "~&") ) { + dt = "_UNSIGNED LONG"; + } else if (func_EndsWith( vname, "##") ) { + dt = "_FLOAT"; + } else if (func_EndsWith( vname, "#") ) { + dt = "DOUBLE"; + } else if (func_EndsWith( vname, "~%&") ) { + dt = "_UNSIGNED _OFFSET"; + } else if (func_EndsWith( vname, "%&") ) { + dt = "_OFFSET"; + } else if (func_EndsWith( vname, "&") ) { + dt = "LONG"; + } else if (func_EndsWith( vname, "!") ) { + dt = "SINGLE"; + } else { + dt = "SINGLE"; + } + DataTypeFromName = dt; +return DataTypeFromName; +} +function func_EndsWith(s/*STRING*/,finds/*STRING*/) { +if (QB.halted()) { return; } +var EndsWith = null; + if (QB.func_Len( finds) > QB.func_Len( s) ) { + EndsWith = False; + return EndsWith; + } + if (QB.func__InStrRev( s, finds) == QB.func_Len( s) -(QB.func_Len( finds) - 1) ) { + EndsWith = True; + } else { + EndsWith = False; + } +return EndsWith; +} +function func_StartsWith(s/*STRING*/,finds/*STRING*/) { +if (QB.halted()) { return; } +var StartsWith = null; + if (QB.func_Len( finds) > QB.func_Len( s) ) { + StartsWith = False; + return StartsWith; + } + if (QB.func_InStr( s, finds) == 1) { + StartsWith = True; + } else { + StartsWith = False; + } +return StartsWith; +} +function func_Join(parts/*STRING*/,startIndex/*INTEGER*/,endIndex/*INTEGER*/,delimiter/*STRING*/) { +if (QB.halted()) { return; } +var Join = null; + if ( endIndex == -1) { + endIndex = QB.func_UBound( parts); + } + var s = ''; // STRING + var i = 0; // INTEGER + for ( i= startIndex; i <= endIndex; i= i + 1) { if (QB.halted()) { return; } + s = s +QB.arrayValue(parts, [ i]).value; + if ( i != QB.func_UBound( parts) ) { + s = s + delimiter; + } + } + Join = s; +return Join; +} +function func_MethodJS(m/*METHOD*/,prefix/*STRING*/) { +if (QB.halted()) { return; } +var MethodJS = null; + var jsname = ''; // STRING + jsname = prefix; + if ( m.type == "FUNCTION" ) { + jsname = jsname +"func_"; + } else { + jsname = jsname +"sub_"; + } + var i = 0; // INTEGER + var c = ''; // STRING + var a = 0; // INTEGER + for ( i= 1; i <= QB.func_Len( m.name); i= i + 1) { if (QB.halted()) { return; } + c = QB.func_Mid( m.name, i, 1); + a = QB.func_Asc( c); + if (( a >= 65 && a <= 90) || ( a >= 97 && a <= 122) || ( a >= 48 && a <= 57) || a == 95 || a == 46) { + jsname = jsname + c; + } + } + if ( m.name == "_Limit" || m.name == "_Delay" || m.name == "Sleep" || m.name == "Input" || m.name == "Print" || m.name == "Fetch" ) { + jsname = "await " + jsname; + } + MethodJS = jsname; +return MethodJS; +} +function func_GXMethodJS(mname/*STRING*/) { +if (QB.halted()) { return; } +var GXMethodJS = null; + var jsname = ''; // STRING + var startIdx = 0; // INTEGER + if (QB.func_InStr( mname, "GXSTR") == 1) { + jsname = "GXSTR."; + startIdx = 7; + } else { + jsname = "GX."; + startIdx = 3; + } + jsname = jsname +QB.func_LCase(QB.func_Mid( mname, startIdx, 1)); + var i = 0; // INTEGER + var c = ''; // STRING + var a = 0; // INTEGER + for ( i= startIdx + 1; i <= QB.func_Len( mname); i= i + 1) { if (QB.halted()) { return; } + c = QB.func_Mid( mname, i, 1); + a = QB.func_Asc( c); + if (( a >= 65 && a <= 90) || ( a >= 97 && a <= 122) || ( a >= 48 && a <= 57) || a == 95 || a == 46) { + jsname = jsname + c; + } + } + if ( mname == "GXMapLoad" || mname == "GXSceneStart" ) { + jsname = "await " + jsname; + } + GXMethodJS = jsname; +return GXMethodJS; +} +async function sub_InitGX() { +if (QB.halted()) { return; } + sub_AddSystemType("GXPOSITION" , "x:LONG,y:LONG"); + sub_AddSystemType("GXDEVICEINPUT" , "deviceId:INTEGER,deviceType:INTEGER,inputType:INTEGER,inputId:INTEGER,inputValue:INTEGER"); + sub_AddGXConst("GX_FALSE"); + sub_AddGXConst("GX_TRUE"); + sub_AddGXConst("GXEVENT_INIT"); + sub_AddGXConst("GXEVENT_UPDATE"); + sub_AddGXConst("GXEVENT_DRAWBG"); + sub_AddGXConst("GXEVENT_DRAWMAP"); + sub_AddGXConst("GXEVENT_DRAWSCREEN"); + sub_AddGXConst("GXEVENT_MOUSEINPUT"); + sub_AddGXConst("GXEVENT_PAINTBEFORE"); + sub_AddGXConst("GXEVENT_PAINTAFTER"); + sub_AddGXConst("GXEVENT_COLLISION_TILE"); + sub_AddGXConst("GXEVENT_COLLISION_ENTITY"); + sub_AddGXConst("GXEVENT_PLAYER_ACTION"); + sub_AddGXConst("GXEVENT_ANIMATE_COMPLETE"); + sub_AddGXConst("GXANIMATE_LOOP"); + sub_AddGXConst("GXANIMATE_SINGLE"); + sub_AddGXConst("GXBG_STRETCH"); + sub_AddGXConst("GXBG_SCROLL"); + sub_AddGXConst("GXBG_WRAP"); + sub_AddGXConst("GXKEY_ESC"); + sub_AddGXConst("GXKEY_1"); + sub_AddGXConst("GXKEY_2"); + sub_AddGXConst("GXKEY_3"); + sub_AddGXConst("GXKEY_4"); + sub_AddGXConst("GXKEY_5"); + sub_AddGXConst("GXKEY_6"); + sub_AddGXConst("GXKEY_7"); + sub_AddGXConst("GXKEY_8"); + sub_AddGXConst("GXKEY_9"); + sub_AddGXConst("GXKEY_0"); + sub_AddGXConst("GXKEY_DASH"); + sub_AddGXConst("GXKEY_EQUALS"); + sub_AddGXConst("GXKEY_BACKSPACE"); + sub_AddGXConst("GXKEY_TAB"); + sub_AddGXConst("GXKEY_Q"); + sub_AddGXConst("GXKEY_W"); + sub_AddGXConst("GXKEY_E"); + sub_AddGXConst("GXKEY_R"); + sub_AddGXConst("GXKEY_T"); + sub_AddGXConst("GXKEY_Y"); + sub_AddGXConst("GXKEY_U"); + sub_AddGXConst("GXKEY_I"); + sub_AddGXConst("GXKEY_O"); + sub_AddGXConst("GXKEY_P"); + sub_AddGXConst("GXKEY_LBRACKET"); + sub_AddGXConst("GXKEY_RBRACKET"); + sub_AddGXConst("GXKEY_ENTER"); + sub_AddGXConst("GXKEY_LCTRL"); + sub_AddGXConst("GXKEY_A"); + sub_AddGXConst("GXKEY_S"); + sub_AddGXConst("GXKEY_D"); + sub_AddGXConst("GXKEY_F"); + sub_AddGXConst("GXKEY_G"); + sub_AddGXConst("GXKEY_H"); + sub_AddGXConst("GXKEY_J"); + sub_AddGXConst("GXKEY_K"); + sub_AddGXConst("GXKEY_L"); + sub_AddGXConst("GXKEY_SEMICOLON"); + sub_AddGXConst("GXKEY_QUOTE"); + sub_AddGXConst("GXKEY_BACKQUOTE"); + sub_AddGXConst("GXKEY_LSHIFT"); + sub_AddGXConst("GXKEY_BACKSLASH"); + sub_AddGXConst("GXKEY_Z"); + sub_AddGXConst("GXKEY_X"); + sub_AddGXConst("GXKEY_C"); + sub_AddGXConst("GXKEY_V"); + sub_AddGXConst("GXKEY_B"); + sub_AddGXConst("GXKEY_N"); + sub_AddGXConst("GXKEY_M"); + sub_AddGXConst("GXKEY_COMMA"); + sub_AddGXConst("GXKEY_PERIOD"); + sub_AddGXConst("GXKEY_SLASH"); + sub_AddGXConst("GXKEY_RSHIFT"); + sub_AddGXConst("GXKEY_NUMPAD_MULTIPLY"); + sub_AddGXConst("GXKEY_SPACEBAR"); + sub_AddGXConst("GXKEY_CAPSLOCK"); + sub_AddGXConst("GXKEY_F1"); + sub_AddGXConst("GXKEY_F2"); + sub_AddGXConst("GXKEY_F3"); + sub_AddGXConst("GXKEY_F4"); + sub_AddGXConst("GXKEY_F5"); + sub_AddGXConst("GXKEY_F6"); + sub_AddGXConst("GXKEY_F7"); + sub_AddGXConst("GXKEY_F8"); + sub_AddGXConst("GXKEY_F9"); + sub_AddGXConst("GXKEY_PAUSE"); + sub_AddGXConst("GXKEY_SCRLK"); + sub_AddGXConst("GXKEY_NUMPAD_7"); + sub_AddGXConst("GXKEY_NUMPAD_8"); + sub_AddGXConst("GXKEY_NUMPAD_9"); + sub_AddGXConst("GXKEY_NUMPAD_MINUS"); + sub_AddGXConst("GXKEY_NUMPAD_4"); + sub_AddGXConst("GXKEY_NUMPAD_5"); + sub_AddGXConst("GXKEY_NUMPAD_6"); + sub_AddGXConst("GXKEY_NUMPAD_PLUS"); + sub_AddGXConst("GXKEY_NUMPAD_1"); + sub_AddGXConst("GXKEY_NUMPAD_2"); + sub_AddGXConst("GXKEY_NUMPAD_3"); + sub_AddGXConst("GXKEY_NUMPAD_0"); + sub_AddGXConst("GXKEY_NUMPAD_PERIOD"); + sub_AddGXConst("GXKEY_F11"); + sub_AddGXConst("GXKEY_F12"); + sub_AddGXConst("GXKEY_NUMPAD_ENTER"); + sub_AddGXConst("GXKEY_RCTRL"); + sub_AddGXConst("GXKEY_NUMPAD_DIVIDE"); + sub_AddGXConst("GXKEY_NUMLOCK"); + sub_AddGXConst("GXKEY_HOME"); + sub_AddGXConst("GXKEY_UP"); + sub_AddGXConst("GXKEY_PAGEUP"); + sub_AddGXConst("GXKEY_LEFT"); + sub_AddGXConst("GXKEY_RIGHT"); + sub_AddGXConst("GXKEY_END"); + sub_AddGXConst("GXKEY_DOWN"); + sub_AddGXConst("GXKEY_PAGEDOWN"); + sub_AddGXConst("GXKEY_INSERT"); + sub_AddGXConst("GXKEY_DELETE"); + sub_AddGXConst("GXKEY_LWIN"); + sub_AddGXConst("GXKEY_RWIN"); + sub_AddGXConst("GXKEY_MENU"); + sub_AddGXConst("GXACTION_MOVE_LEFT"); + sub_AddGXConst("GXACTION_MOVE_RIGHT"); + sub_AddGXConst("GXACTION_MOVE_UP"); + sub_AddGXConst("GXACTION_MOVE_DOWN"); + sub_AddGXConst("GXACTION_JUMP"); + sub_AddGXConst("GXACTION_JUMP_RIGHT"); + sub_AddGXConst("GXACTION_JUMP_LEFT"); + sub_AddGXConst("GXSCENE_FOLLOW_NONE"); + sub_AddGXConst("GXSCENE_FOLLOW_ENTITY_CENTER"); + sub_AddGXConst("GXSCENE_FOLLOW_ENTITY_CENTER_X"); + sub_AddGXConst("GXSCENE_FOLLOW_ENTITY_CENTER_Y"); + sub_AddGXConst("GXSCENE_FOLLOW_ENTITY_CENTER_X_POS"); + sub_AddGXConst("GXSCENE_FOLLOW_ENTITY_CENTER_X_NEG"); + sub_AddGXConst("GXSCENE_CONSTRAIN_NONE"); + sub_AddGXConst("GXSCENE_CONSTRAIN_TO_MAP"); + sub_AddGXConst("GXFONT_DEFAULT"); + sub_AddGXConst("GXFONT_DEFAULT_BLACK"); + sub_AddGXConst("GXDEVICE_KEYBOARD"); + sub_AddGXConst("GXDEVICE_MOUSE"); + sub_AddGXConst("GXDEVICE_CONTROLLER"); + sub_AddGXConst("GXDEVICE_BUTTON"); + sub_AddGXConst("GXDEVICE_AXIS"); + sub_AddGXConst("GXDEVICE_WHEEL"); + sub_AddGXConst("GXTYPE_ENTITY"); + sub_AddGXConst("GXTYPE_FONT"); + sub_AddGXMethod("SUB" , "GXSleep"); + sub_AddGXMethod("FUNCTION" , "GXMouseX"); + sub_AddGXMethod("FUNCTION" , "GXMouseY"); + sub_AddGXMethod("FUNCTION" , "GXSoundLoad"); + sub_AddGXMethod("SUB" , "GXSoundPlay"); + sub_AddGXMethod("SUB" , "GXSoundRepeat"); + sub_AddGXMethod("SUB" , "GXSoundVolume"); + sub_AddGXMethod("SUB" , "GXSoundPause"); + sub_AddGXMethod("SUB" , "GXSoundStop"); + sub_AddGXMethod("SUB" , "GXSoundMuted"); + sub_AddGXMethod("FUNCTION" , "GXSoundMuted"); + sub_AddGXMethod("SUB" , "GXEntityAnimate"); + sub_AddGXMethod("SUB" , "GXEntityAnimateStop"); + sub_AddGXMethod("SUB" , "GXEntityAnimateMode"); + sub_AddGXMethod("FUNCTION" , "GXEntityAnimateMode"); + sub_AddGXMethod("FUNCTION" , "GXScreenEntityCreate"); + sub_AddGXMethod("FUNCTION" , "GXEntityCreate"); + sub_AddGXMethod("SUB" , "GXEntityCreate"); + sub_AddGXMethod("SUB" , "GXEntityVisible"); + sub_AddGXMethod("SUB" , "GXEntityMove"); + sub_AddGXMethod("SUB" , "GXEntityPos"); + sub_AddGXMethod("SUB" , "GXEntityVX"); + sub_AddGXMethod("FUNCTION" , "GXEntityVX"); + sub_AddGXMethod("SUB" , "GXEntityVY"); + sub_AddGXMethod("FUNCTION" , "GXEntityVY"); + sub_AddGXMethod("FUNCTION" , "GXEntityX"); + sub_AddGXMethod("FUNCTION" , "GXEntityY"); + sub_AddGXMethod("FUNCTION" , "GXEntityWidth"); + sub_AddGXMethod("FUNCTION" , "GXEntityHeight"); + sub_AddGXMethod("SUB" , "GXEntityFrameNext"); + sub_AddGXMethod("SUB" , "GXEntityFrameSet"); + sub_AddGXMethod("SUB" , "GXEntityType"); + sub_AddGXMethod("FUNCTION" , "GXEntityType"); + sub_AddGXMethod("FUNCTION" , "GXEntityUID$"); + sub_AddGXMethod("FUNCTION" , "GXFontUID$"); + sub_AddGXMethod("FUNCTION" , "GX"); + sub_AddGXMethod("SUB" , "GXEntityApplyGravity"); + sub_AddGXMethod("FUNCTION" , "GXEntityApplyGravity"); + sub_AddGXMethod("SUB" , "GXEntityCollisionOffset"); + sub_AddGXMethod("FUNCTION" , "GXEntityCollisionOffsetLeft"); + sub_AddGXMethod("FUNCTION" , "GXEntityCollisionOffsetTop"); + sub_AddGXMethod("FUNCTION" , "GXEntityCollisionOffsetRight"); + sub_AddGXMethod("FUNCTION" , "GXEntityCollisionOffsetBottom"); + sub_AddGXMethod("SUB" , "GXFullScreen"); + sub_AddGXMethod("FUNCTION" , "GXFullScreen"); + sub_AddGXMethod("FUNCTION" , "GXBackgroundAdd"); + sub_AddGXMethod("SUB" , "GXBackgroundY"); + sub_AddGXMethod("SUB" , "GXBackgroundHeight"); + sub_AddGXMethod("SUB" , "GXBackgroundClear"); + sub_AddGXMethod("SUB" , "GXSceneEmbedded"); + sub_AddGXMethod("FUNCTION" , "GXSceneEmbedded"); + sub_AddGXMethod("SUB" , "GXSceneCreate"); + sub_AddGXMethod("SUB" , "GXSceneWindowSize"); + sub_AddGXMethod("SUB" , "GXSceneScale"); + sub_AddGXMethod("SUB" , "GXSceneResize"); + sub_AddGXMethod("SUB" , "GXSceneDestroy"); + sub_AddGXMethod("SUB" , "GXCustomDraw"); + sub_AddGXMethod("FUNCTION" , "GXCustomDraw"); + sub_AddGXMethod("SUB" , "GXFrameRate"); + sub_AddGXMethod("FUNCTION" , "GXFrameRate"); + sub_AddGXMethod("FUNCTION" , "GXFrame"); + sub_AddGXMethod("SUB" , "GXSceneDraw"); + sub_AddGXMethod("SUB" , "GXSceneMove"); + sub_AddGXMethod("SUB" , "GXScenePos"); + sub_AddGXMethod("FUNCTION" , "GXSceneX"); + sub_AddGXMethod("FUNCTION" , "GXSceneY"); + sub_AddGXMethod("FUNCTION" , "GXSceneWidth"); + sub_AddGXMethod("FUNCTION" , "GXSceneHeight"); + sub_AddGXMethod("FUNCTION" , "GXSceneColumns"); + sub_AddGXMethod("FUNCTION" , "GXSceneRows"); + sub_AddGXMethod("SUB" , "GXSceneStart"); + sub_AddGXMethod("SUB" , "GXSceneUpdate"); + sub_AddGXMethod("SUB" , "GXSceneFollowEntity"); + sub_AddGXMethod("SUB" , "GXSceneConstrain"); + sub_AddGXMethod("SUB" , "GXSceneStop"); + sub_AddGXMethod("SUB" , "GXMapCreate"); + sub_AddGXMethod("FUNCTION" , "GXMapColumns"); + sub_AddGXMethod("FUNCTION" , "GXMapRows"); + sub_AddGXMethod("FUNCTION" , "GXMapLayers"); + sub_AddGXMethod("SUB" , "GXMapLayerVisible"); + sub_AddGXMethod("FUNCTION" , "GXMapLayerVisible"); + sub_AddGXMethod("SUB" , "GXMapLayerAdd"); + sub_AddGXMethod("SUB" , "GXMapLayerInsert"); + sub_AddGXMethod("SUB" , "GXMapLayerRemove"); + sub_AddGXMethod("SUB" , "GXMapResize"); + sub_AddGXMethod("SUB" , "GXMapDraw"); + sub_AddGXMethod("SUB" , "GXMapTilePosAt"); + sub_AddGXMethod("SUB" , "GXMapTile"); + sub_AddGXMethod("FUNCTION" , "GXMapTile"); + sub_AddGXMethod("FUNCTION" , "GXMapTileDepth"); + sub_AddGXMethod("SUB" , "GXMapTileAdd"); + sub_AddGXMethod("SUB" , "GXMapTileRemove"); + sub_AddGXMethod("FUNCTION" , "GXMapVersion"); + sub_AddGXMethod("SUB" , "GXMapSave"); + sub_AddGXMethod("SUB" , "GXMapLoad"); + sub_AddGXMethod("FUNCTION" , "GXMapIsometric"); + sub_AddGXMethod("SUB" , "GXMapIsometric"); + sub_AddGXMethod("SUB" , "GXSpriteDraw"); + sub_AddGXMethod("SUB" , "GXSpriteDrawScaled"); + sub_AddGXMethod("SUB" , "GXTilesetCreate"); + sub_AddGXMethod("SUB" , "GXTilesetReplaceImage"); + sub_AddGXMethod("SUB" , "GXTilesetLoad"); + sub_AddGXMethod("SUB" , "GXTilesetSave"); + sub_AddGXMethod("SUB" , "GXTilesetPos"); + sub_AddGXMethod("FUNCTION" , "GXTilesetWidth"); + sub_AddGXMethod("FUNCTION" , "GXTilesetHeight"); + sub_AddGXMethod("FUNCTION" , "GXTilesetColumns"); + sub_AddGXMethod("FUNCTION" , "GXTilesetRows"); + sub_AddGXMethod("FUNCTION" , "GXTilesetFilename"); + sub_AddGXMethod("FUNCTION" , "GXTilesetImage"); + sub_AddGXMethod("SUB" , "GXTilesetAnimationCreate"); + sub_AddGXMethod("SUB" , "GXTilesetAnimationAdd"); + sub_AddGXMethod("SUB" , "GXTilesetAnimationRemove"); + sub_AddGXMethod("FUNCTION" , "GXTilesetAnimationFrames"); + sub_AddGXMethod("FUNCTION" , "GXTilesetAnimationSpeed"); + sub_AddGXMethod("SUB" , "GXTilesetAnimationSpeed"); + sub_AddGXMethod("FUNCTION" , "GXFontCreate"); + sub_AddGXMethod("SUB" , "GXFontCreate"); + sub_AddGXMethod("FUNCTION" , "GXFontWidth"); + sub_AddGXMethod("FUNCTION" , "GXFontHeight"); + sub_AddGXMethod("FUNCTION" , "GXFontCharSpacing"); + sub_AddGXMethod("SUB" , "GXFontCharSpacing"); + sub_AddGXMethod("FUNCTION" , "GXFontLineSpacing"); + sub_AddGXMethod("SUB" , "GXFontLineSpacing"); + sub_AddGXMethod("SUB" , "GXDrawText"); + sub_AddGXMethod("FUNCTION" , "GXDebug"); + sub_AddGXMethod("SUB" , "GXDebug"); + sub_AddGXMethod("FUNCTION" , "GXDebugScreenEntities"); + sub_AddGXMethod("SUB" , "GXDebugScreenEntities"); + sub_AddGXMethod("FUNCTION" , "GXDebugFont"); + sub_AddGXMethod("SUB" , "GXDebugFont"); + sub_AddGXMethod("FUNCTION" , "GXDebugTileBorderColor"); + sub_AddGXMethod("SUB" , "GXDebugTileBorderColor"); + sub_AddGXMethod("FUNCTION" , "GXDebugEntityBorderColor"); + sub_AddGXMethod("SUB" , "GXDebugEntityBorderColor"); + sub_AddGXMethod("FUNCTION" , "GXDebugEntityCollisionColor"); + sub_AddGXMethod("SUB" , "GXDebugEntityCollisionColor"); + sub_AddGXMethod("SUB" , "GXKeyInput"); + sub_AddGXMethod("FUNCTION" , "GXKeyDown"); + sub_AddGXMethod("SUB" , "GXDeviceInputDetect"); + sub_AddGXMethod("FUNCTION" , "GXDeviceInputTest"); + sub_AddGXMethod("FUNCTION" , "GXDeviceName"); + sub_AddGXMethod("FUNCTION" , "GXDeviceTypeName"); + sub_AddGXMethod("FUNCTION" , "GXInputTypeName"); + sub_AddGXMethod("FUNCTION" , "GXKeyButtonName"); + sub_AddGXConst("GX_CR"); + sub_AddGXConst("GX_LF"); + sub_AddGXConst("GX_CRLF"); + sub_AddGXMethod("FUNCTION" , "GXSTR_LPad"); + sub_AddGXMethod("FUNCTION" , "GXSTR_RPad"); + sub_AddGXMethod("FUNCTION" , "GXSTR_Replace"); +} +async function sub_InitQBMethods() { +if (QB.halted()) { return; } + sub_AddQBMethod("FUNCTION" , "_Alpha32"); + sub_AddQBMethod("FUNCTION" , "_Atan2"); + sub_AddQBMethod("FUNCTION" , "_Blue"); + sub_AddQBMethod("FUNCTION" , "_Blue32"); + sub_AddQBMethod("SUB" , "_Delay"); + sub_AddQBMethod("FUNCTION" , "_FontWidth"); + sub_AddQBMethod("FUNCTION" , "_Green"); + sub_AddQBMethod("FUNCTION" , "_Green32"); + sub_AddQBMethod("FUNCTION" , "_Height"); + sub_AddQBMethod("FUNCTION" , "_InStrRev"); + sub_AddQBMethod("SUB" , "_Limit"); + sub_AddQBMethod("FUNCTION" , "_KeyDown"); + sub_AddQBMethod("FUNCTION" , "_KeyHit"); + sub_AddQBMethod("FUNCTION" , "_MouseButton"); + sub_AddQBMethod("FUNCTION" , "_MouseInput"); + sub_AddQBMethod("FUNCTION" , "_MouseX"); + sub_AddQBMethod("FUNCTION" , "_MouseY"); + sub_AddQBMethod("FUNCTION" , "_NewImage"); + sub_AddQBMethod("FUNCTION" , "_Pi"); + sub_AddQBMethod("SUB" , "_PrintString"); + sub_AddQBMethod("FUNCTION" , "_PrintWidth"); + sub_AddQBMethod("FUNCTION" , "_Red"); + sub_AddQBMethod("FUNCTION" , "_Red32"); + sub_AddQBMethod("FUNCTION" , "_RGB"); + sub_AddQBMethod("FUNCTION" , "_RGB32"); + sub_AddQBMethod("FUNCTION" , "_Round"); + sub_AddQBMethod("FUNCTION" , "_ScreenExists"); + sub_AddQBMethod("SUB" , "_Title"); + sub_AddQBMethod("FUNCTION" , "_Trim"); + sub_AddQBMethod("FUNCTION" , "_Width"); + sub_AddQBMethod("FUNCTION" , "Abs"); + sub_AddQBMethod("FUNCTION" , "Asc"); + sub_AddQBMethod("FUNCTION" , "Atn"); + sub_AddQBMethod("FUNCTION" , "Chr$"); + sub_AddQBMethod("SUB" , "Circle"); + sub_AddQBMethod("SUB" , "Cls"); + sub_AddQBMethod("SUB" , "Color"); + sub_AddQBMethod("FUNCTION" , "Command$"); + sub_AddQBMethod("FUNCTION" , "Cos"); + sub_AddQBMethod("FUNCTION" , "Exp"); + sub_AddQBMethod("FUNCTION" , "Fix"); + sub_AddQBMethod("SUB" , "Input"); + sub_AddQBMethod("FUNCTION" , "InKey$"); + sub_AddQBMethod("FUNCTION" , "InStr"); + sub_AddQBMethod("FUNCTION" , "Int"); + sub_AddQBMethod("FUNCTION" , "Left$"); + sub_AddQBMethod("FUNCTION" , "LCase$"); + sub_AddQBMethod("FUNCTION" , "Len"); + sub_AddQBMethod("SUB" , "Line"); + sub_AddQBMethod("SUB" , "Locate"); + sub_AddQBMethod("FUNCTION" , "Log"); + sub_AddQBMethod("FUNCTION" , "LTrim$"); + sub_AddQBMethod("FUNCTION" , "Mid$"); + sub_AddQBMethod("SUB" , "Print"); + sub_AddQBMethod("SUB" , "PSet"); + sub_AddQBMethod("FUNCTION" , "Right$"); + sub_AddQBMethod("FUNCTION" , "RTrim$"); + sub_AddQBMethod("FUNCTION" , "Rnd"); + sub_AddQBMethod("SUB" , "Screen"); + sub_AddQBMethod("FUNCTION" , "Sgn"); + sub_AddQBMethod("FUNCTION" , "Sin"); + sub_AddQBMethod("SUB" , "Sleep"); + sub_AddQBMethod("FUNCTION" , "Sqr"); + sub_AddQBMethod("FUNCTION" , "Str$"); + sub_AddQBMethod("SUB" , "Swap"); + sub_AddQBMethod("FUNCTION" , "Tan"); + sub_AddQBMethod("FUNCTION" , "Timer"); + sub_AddQBMethod("FUNCTION" , "UBound"); + sub_AddQBMethod("FUNCTION" , "UCase$"); + sub_AddQBMethod("FUNCTION" , "Val"); + sub_AddSystemType("FETCHRESPONSE" , "ok:INTEGER,status:INTEGER,statusText:STRING,text:STRING"); + sub_AddQBMethod("FUNCTION" , "Fetch"); + sub_AddQBMethod("FUNCTION" , "FromJSON"); + sub_AddQBMethod("FUNCTION" , "ToJSON"); +} +this.compile = function(src) { + sub_QBToJS(src, TEXT); + var js = ''; + for (var i=1; i<= QB.func_UBound(jsLines); i++) { + js += QB.arrayValue(jsLines, [i]).value.text + '\n'; + } + return js; +}; +this.getWarnings = function() { + var w = []; + for (var i=1; i <= QB.func_UBound(warnings); i++) { + w.push({ + line: QB.arrayValue(warnings, [i]).value.line, + text: QB.arrayValue(warnings, [i]).value.text + }); + } + return w; +}; +}; diff --git a/tools/qb2js.bas b/tools/qb2js.bas new file mode 100644 index 0000000..918c8a1 --- /dev/null +++ b/tools/qb2js.bas @@ -0,0 +1,2365 @@ +Option _Explicit +$Console:Only +'$ExeIcon:'./../gx/resource/gx.ico' + +Const FILE = 1 +Const TEXT = 2 +Const False = 0 +Const True = Not False + +Type CodeLine + line As Integer + text As String +End Type + +Type Method + line As Integer + type As String + returnType As String + name As String + uname As String + argc As Integer + args As String + jsname As String +End Type + +Type Argument + name As String + type As String +End Type + +Type QBType + line As Integer + name As String + argc As Integer + args As String +End Type + +Type Variable + type As String + name As String + jsname As String + isConst As Integer + isArray As Integer + arraySize As Integer + typeId As Integer +End Type + +ReDim Shared As CodeLine lines(0) +ReDim Shared As CodeLine jsLines(0) +ReDim Shared As Method methods(0) +ReDim Shared As QBType types(0) +ReDim Shared As Variable typeVars(0) +ReDim Shared As Variable globalVars(0) +ReDim Shared As Variable localVars(0) +ReDim Shared As CodeLine warnings(0) +Dim Shared As String currentMethod +Dim Shared As Integer programMethods + + +' Only execute the conversion from the native version if we have been passed the +' source file to convert on the command line +If Command$ <> "" Then + QBToJS Command$, FILE + PrintJS + System +End If + +Sub QBToJS (source As String, sourceType As Integer) + ' Reset data structures + ReDim As CodeLine lines(0) + ReDim As CodeLine jsLines(0) + ReDim As Method methods(0) + ReDim As QBType types(0) + ReDim As Variable typeVars(0) + ReDim As Variable globalVars(0) + ReDim As Variable localVars(0) + ReDim As CodeLine warnings(0) + currentMethod = "" + programMethods = 0 + + If sourceType = FILE Then + ReadLinesFromFile source + Else + ReadLinesFromText source + End If + + FindMethods + programMethods = UBound(methods) + InitGX + InitQBMethods + + ' Detect whether we are converting ourself to javascript. If so: + ' 1) Place the converted code into an object named QB6Compiler + ' 2) Forgo initializing the game events and default screen + ' 3) Add an externally callable javascript function named "compile" + ' which will allow us to call the converter from a web application + Dim selfConvert As Integer + Dim isGX As Integer: isGX = False + If sourceType = FILE Then selfConvert = EndsWith(source, "qb2js.bas") + + If selfConvert Then + AddJSLine 0, "var QBCompiler = new function() {" + + ElseIf sourceType = FILE Then + AddJSLine 0, "async function init() {" + End If + + If Not selfConvert Then AddJSLine 0, "QB.start();" + + If Not selfConvert Then + Dim mtest As Method + If FindMethod("GXOnGameEvent", mtest, "SUB") Then + AddJSLine 0, " await GX.registerGameEvents(sub_GXOnGameEvent);" + isGX = True + Else + AddJSLine 0, " await GX.registerGameEvents(function(e){});" + AddJSLine 0, " QB.sub_Screen(0);" + End If + End If + AddJSLine 0, "" + + ConvertLines 1, MainEnd, "" + If Not selfConvert And Not isGX Then AddJSLine 0, "QB.end();" + ConvertMethods + + + If selfConvert Then + AddJSLine 0, "this.compile = function(src) {" + AddJSLine 0, " sub_QBToJS(src, TEXT);" + AddJSLine 0, " var js = '';" + AddJSLine 0, " for (var i=1; i<= QB.func_UBound(jsLines); i++) {" + AddJSLine 0, " js += QB.arrayValue(jsLines, [i]).value.text + '\n';" + AddJSLine 0, " }" + AddJSLine 0, " return js;" + AddJSLine 0, "};" + AddJSLine 0, "this.getWarnings = function() {" + AddJSLine 0, " var w = [];" + AddJSLine 0, " for (var i=1; i <= QB.func_UBound(warnings); i++) {" + AddJSLine 0, " w.push({" + AddJSLine 0, " line: QB.arrayValue(warnings, [i]).value.line," + AddJSLine 0, " text: QB.arrayValue(warnings, [i]).value.text" + AddJSLine 0, " });" + AddJSLine 0, " }" + AddJSLine 0, " return w;" + AddJSLine 0, "};" + AddJSLine 0, "};" + ElseIf sourceType = FILE Then + AddJSLine 0, "};" + End If +End Sub + +Sub PrintJS + Dim i As Integer + For i = 1 To UBound(jsLines) + Print jsLines(i).text + Next i +End Sub + +Sub ConvertLines (firstLine As Integer, lastLine As Integer, functionName As String) + Dim typeMode As Integer: typeMode = False + Dim jsMode As Integer: jsMode = False + Dim i As Integer + Dim indent As Integer + Dim tempIndent As Integer + Dim m As Method + Dim totalIndent As Integer + totalIndent = 1 + Dim caseCount As Integer + Dim loopMode(100) As Integer ' TODO: only supports 100 levels of do/loop nesting + Dim loopLevel As Integer + Dim caseVar As String + Dim currType As Integer + + For i = firstLine To lastLine + indent = 0 + tempIndent = 0 + Dim l As String + l = _Trim$(lines(i).text) + ReDim As String parts(0) + Dim c As Integer + c = SLSplit(l, parts()) + + Dim js As String + js = "" + Dim first As String + first = UCase$(parts(1)) + + If jsMode = True Then + If first = "$END" Then + jsMode = False + AddJSLine 0, "//-------- END JS native code block --------" + Else + AddJSLine i, lines(i).text + End If + + ElseIf typeMode = True Then + If first = "END" Then + Dim second As String: second = UCase$(parts(2)) + If second = "TYPE" Then + typeMode = False + End If + Else + Dim tvar As Variable + tvar.typeId = currType + tvar.name = parts(1) + tvar.type = UCase$(parts(3)) + If tvar.type = "_UNSIGNED" Then tvar.type = tvar.type + " " + UCase$(parts(4)) + AddVariable tvar, typeVars() + End If + Else + If first = "CONST" Then + js = "const " + parts(2) + " = " + ConvertExpression(Join(parts(), 4, -1, " ")) + ";" + AddConst parts(2) + + ElseIf first = "DIM" Or first = "REDIM" Or first = "STATIC" Then + js = DeclareVar(parts()) + + + ElseIf first = "SELECT" Then + caseVar = GenJSVar '"___c" + _Trim$(Str$(_Round(Rnd * 10000000))) + js = "var " + caseVar + " = " + ConvertExpression(Join(parts(), 3, -1, " ")) + ";" + GX_CRLF + js = js + "switch (" + caseVar + ") {" + indent = 1 + caseCount = 0 + + ElseIf first = "CASE" Then + If caseCount > 0 Then js = "break;" + GX_LF + If UCase$(parts(2)) = "ELSE" Then + js = js + "default:" + ElseIf UCase$(parts(2)) = "IS" Then + js = js + "case " + caseVar + " " + ConvertExpression(Join(parts(), 3, -1, " ")) + ":" + Else + 'js = js + "case " + ConvertExpression(parts(2)) + ":" + ReDim As String caseParts(0) + Dim cscount As Integer + cscount = ListSplit(Join(parts(), 2, -1, " "), caseParts()) + Dim ci As Integer + For ci = 1 To cscount + If ci > 1 Then js = js + GX_CRLF + js = js + "case " + ConvertExpression(caseParts(ci)) + ":" + Next ci + End If + caseCount = caseCount + 1 + + ElseIf first = "FOR" Then + Dim fstep As String: fstep = "1" + Dim eqIdx As Integer + Dim toIdx As Integer + Dim stepIdx As Integer + Dim fcond As String: fcond = " <= " + stepIdx = 0 + Dim fi As Integer + For fi = 2 To UBound(parts) + Dim fword As String + fword = UCase$(parts(fi)) + If fword = "=" Then + eqIdx = fi + ElseIf fword = "TO" Then + toIdx = fi + ElseIf fword = "STEP" Then + stepIdx = fi + fstep = ConvertExpression(Join(parts(), fi + 1, -1, " ")) + End If + Next fi + Dim fvar As String + fvar = ConvertExpression(Join(parts(), 2, eqIdx - 1, " ")) + Dim sval As String + sval = ConvertExpression(Join(parts(), eqIdx + 1, toIdx - 1, " ")) + Dim uval As String + uval = ConvertExpression(Join(parts(), toIdx + 1, stepIdx - 1, " ")) + + 'If Val(fstep) < 0 Then fcond = " >= " + If Left$(_Trim$(fstep), 1) = "-" Then fcond = " >= " + + js = "for (" + fvar + "=" + sval + "; " + fvar + fcond + uval + "; " + fvar + "=" + fvar + " + " + fstep + ") {" + js = js + " if (QB.halted()) { return; }" + + 'If UBound(parts) = 8 Then fstep = parts(8) + 'js = "for (" + parts(2) + "=" + parts(4) + "; " + parts(2) + " <= " + ConvertExpression(parts(6)) + "; " + parts(2) + "=" + parts(2) + "+" + fstep + ") {" + indent = 1 + + ElseIf first = "IF" Then + Dim thenIndex As Integer + For thenIndex = 2 To UBound(parts) + If UCase$(parts(thenIndex)) = "THEN" Then Exit For + Next thenIndex + + js = "if (" + ConvertExpression(Join(parts(), 2, thenIndex - 1, " ")) + ") {" + indent = 1 + + ElseIf first = "ELSEIF" Then + js = "} else if (" + ConvertExpression(Join(parts(), 2, UBound(parts) - 1, " ")) + ") {" + tempIndent = -1 + + ElseIf first = "ELSE" Then + js = "} else {" + tempIndent = -1 + + ElseIf first = "NEXT" Then + js = js + "}" + indent = -1 + + ElseIf first = "END" Then + If UBound(parts) = 1 Then + js = "QB.halt(); return;" + + 'js = "// END" + 'AddWarning i, "End is not currently supported in this context, ignoring." + Else + If UCase$(parts(2)) = "SELECT" Then js = "break;" + js = js + "}" + indent = -1 + End If + + ElseIf first = "SYSTEM" Then + js = "QB.halt(); return;" + + ElseIf first = "$IF" Then + If UBound(parts) = 2 Then + If UCase$(parts(2)) = "JS" Or UCase$(parts(2)) = "JAVASCRIPT" Then + jsMode = True + js = "//-------- BEGIN JS native code block --------" + End If + End If + + ElseIf first = "DO" Then + loopLevel = loopLevel + 1 + If UBound(parts) > 1 Then + If UCase$(parts(2)) = "WHILE" Then + js = "while (" + ConvertExpression(Join(parts(), 3, -1, " ")) + ") {" + Else + js = "while (!(" + ConvertExpression(Join(parts(), 3, -1, " ")) + ")) {" + End If + loopMode(loopLevel) = 1 + Else + js = "do {" + loopMode(loopLevel) = 2 + End If + indent = 1 + js = js + " if (QB.halted()) { return; }" + + + ElseIf first = "WHILE" Then + loopLevel = loopLevel + 1 + js = "while (" + ConvertExpression(Join(parts(), 2, -1, " ")) + ") {" + indent = 1 + js = js + " if (QB.halted()) { return; }" + + ElseIf first = "WEND" Then + js = "}" + loopLevel = loopLevel - 1 + indent = -1 + + ElseIf first = "LOOP" Then + If loopMode(loopLevel) = 1 Then + js = "}" + Else + js = "} while ((" + If UBound(parts) < 2 Then + js = js + "1));" + Else + If UCase$(parts(2)) = "UNTIL" Then js = "} while (!(" + js = js + ConvertExpression(Join(parts(), 3, UBound(parts), " ")) + "))" + End If + End If + loopLevel = loopLevel - 1 + indent = -1 + + ElseIf first = "_CONTINUE" Then + js = "continue;" + + ElseIf UCase$(l) = "EXIT FUNCTION" Then + js = "return " + functionName + ";" + + ElseIf UCase$(l) = "EXIT SUB" Then + js = "return;" + + ElseIf first = "EXIT" Then + js = "break;" + + ElseIf first = "TYPE" Then + typeMode = True + Dim qbtype As QBType + qbtype.line = i + qbtype.name = UCase$(parts(2)) + AddType qbtype + currType = UBound(types) + + ElseIf first = "CALL" Then + Dim subline As String + subline = _Trim$(Join(parts(), 2, -1, " ")) + + Dim subend As Integer + subend = InStr(subline, "(") + + Dim subname As String + If subend = 0 Then + subname = subline + Else + subname = Left$(subline, subend - 1) + End If + + If FindMethod(subname, m, "SUB") Then + Dim subargs As String + subargs = Mid$(subline, Len(subname) + 2, Len(subline) - Len(subname) - 2) + js = ConvertSub(m, subargs) + Else + 'js = "// " + l + AddWarning i, "Missing Sub [" + subname + "], ignoring Call command" + End If + + ElseIf c > 2 Then + Dim assignment As Integer + assignment = 0 + Dim j As Integer + For j = 1 To UBound(parts) + If parts(j) = "=" Then + assignment = j + Exit For + End If + Next j + + If assignment > 0 Then + 'This is a variable assignment + js = RemoveSuffix(ConvertExpression(Join(parts(), 1, assignment - 1, " "))) + " = " + ConvertExpression(Join(parts(), assignment + 1, -1, " ")) + ";" + + Else + If FindMethod(parts(1), m, "SUB") Then + js = ConvertSub(m, Join(parts(), 2, -1, " ")) + Else + js = "// " + l + AddWarning i, "Missing/unsupported sub or syntax error" + End If + End If + + + Else + If FindMethod(parts(1), m, "SUB") Then + js = ConvertSub(m, Join(parts(), 2, -1, " ")) + Else + js = "// " + l + AddWarning i, "Missing/unsupported sub or syntax error" + End If + End If + + If (indent < 0) Then totalIndent = totalIndent + indent + '*Print GXSTR_LPad("", " ", (totalIndent + tempIndent) * 3) + js + AddJSLine i, GXSTR_LPad("", " ", (totalIndent + tempIndent) * 3) + js + If (indent > 0) Then totalIndent = totalIndent + indent + + End If + + Next i + +End Sub + +Function ConvertSub$ (m As Method, args As String) + ' This actually converts the parameters passed to the sub + Dim js As String + + ' Let's handle the weirdo Line Input command which has a space + If m.name = "Line" Then + Dim parts(0) As String + Dim plen As Integer + plen = SLSplit(args, parts()) + If plen > 0 Then + If UCase$(parts(1)) = "INPUT" Then + m.name = "Line Input" + m.jsname = "await QB.sub_LineInput" + args = Join(parts(), 2, -1, " ") + End If + End If + End If + + ' Handle special cases for methods which take ranges and optional parameters + If m.name = "Line" Then + js = m.jsname + "(" + ConvertLine(args) + ");" + + ElseIf m.name = "PSet" Or m.name = "Circle" Then + js = m.jsname + "(" + ConvertPSet(args) + ");" + + ElseIf m.name = "_PrintString" Then + js = m.jsname + "(" + ConvertPrintString(args) + ");" + + ElseIf m.name = "Print" Then + js = m.jsname + "(" + ConvertPrint(args) + ");" + + ElseIf m.name = "Input" Or m.name = "Line Input" Then + js = ConvertInput(m, args) + + ElseIf m.name = "Swap" Then + js = ConvertSwap(m, args) + + Else + js = m.jsname + "(" + ConvertExpression(args) + ");" + End If + + ConvertSub = js +End Function + +Function ConvertLine$ (args As String) + ' TODO: This does not yet handle dash patterns + Dim firstParam As String + Dim theRest As String + Dim idx As Integer + Dim sstep As String + Dim estep As String + sstep = "false" + estep = "false" + + idx = FindParamChar(args, ",") + If idx = -1 Then + firstParam = args + theRest = "" + Else + firstParam = Left$(args, idx - 1) + theRest = Right$(args, Len(args) - idx) + End If + + idx = FindParamChar(firstParam, "-") + Dim startCord As String + Dim endCord As String + If idx = -1 Then + endCord = firstParam + Else + startCord = Left$(firstParam, idx - 1) + endCord = Right$(firstParam, Len(firstParam) - idx) + End If + + If UCase$(_Trim$(Left$(startCord, 4))) = "STEP" Then + sstep = "true" + End If + If UCase$(_Trim$(Left$(endCord, 4))) = "STEP" Then + estep = "true" + End If + + idx = InStr(startCord, "(") + startCord = Right$(startCord, Len(startCord) - idx) + idx = _InStrRev(startCord, ")") + startCord = Left$(startCord, idx - 1) + startCord = ConvertExpression(startCord) + If (_Trim$(startCord) = "") Then startCord = "undefined, undefined" + + idx = InStr(endCord, "(") + endCord = Right$(endCord, Len(endCord) - idx) + idx = _InStrRev(endCord, ")") + endCord = Left$(endCord, idx - 1) + endCord = ConvertExpression(endCord) + + theRest = ConvertExpression(theRest) + theRest = GXSTR_Replace(theRest, " BF", " " + Chr$(34) + "BF" + Chr$(34)) + theRest = GXSTR_Replace(theRest, " B", " " + Chr$(34) + "B" + Chr$(34)) + + ConvertLine = sstep + ", " + startCord + ", " + estep + ", " + endCord + ", " + theRest +End Function + +Function ConvertPSet$ (args As String) + Dim firstParam As String + Dim theRest As String + Dim idx As Integer + Dim sstep As String + sstep = "false" + + idx = FindParamChar(args, ",") + If idx = -1 Then + firstParam = args + theRest = "" + Else + firstParam = Left$(args, idx - 1) + theRest = Right$(args, Len(args) - idx) + End If + + If UCase$(_Trim$(Left$(firstParam, 4))) = "STEP" Then + sstep = "true" + End If + + idx = InStr(firstParam, "(") + firstParam = Right$(firstParam, Len(firstParam) - idx) + idx = _InStrRev(firstParam, ")") + firstParam = Left$(firstParam, idx - 1) + firstParam = ConvertExpression(firstParam) + If (_Trim$(firstParam) = "") Then firstParam = "undefined, undefined" + + theRest = ConvertExpression(theRest) + + ConvertPSet = sstep + ", " + firstParam + ", " + theRest +End Function + +Function ConvertPrint$ (args As String) + Dim pcount As Integer + Dim parts(0) As String + pcount = PrintSplit(args, parts()) + + Dim js As String + js = "[" + + Dim i As Integer + For i = 1 To pcount + If i > 1 Then js = js + "," + + If parts(i) = "," Then + js = js + "QB.COLUMN_ADVANCE" + + ElseIf parts(i) = ";" Then + js = js + "QB.PREVENT_NEWLINE" + + Else + js = js + ConvertExpression(parts(i)) + End If + Next i + + ConvertPrint = js + "]" +End Function + +Function ConvertPrintString$ (args As String) + Dim firstParam As String + Dim theRest As String + Dim idx As Integer + + idx = FindParamChar(args, ",") + If idx = -1 Then + firstParam = args + theRest = "" + Else + firstParam = Left$(args, idx - 1) + theRest = Right$(args, Len(args) - idx) + End If + + idx = InStr(firstParam, "(") + firstParam = Right$(firstParam, Len(firstParam) - idx) + idx = _InStrRev(firstParam, ")") + firstParam = Left$(firstParam, idx - 1) + + ConvertPrintString = ConvertExpression(firstParam) + ", " + ConvertExpression(theRest) +End Function + +'Function ConvertInput$ (m As Method, args As String) +' Dim js As String +' Dim vname As String +' vname = GenJSVar '"___i" + _Trim$(Str$(_Round(Rnd * 10000000))) +' js = "var " + vname + " = new Array(1);" + GX_LF +' js = js + m.jsname + "(" + vname + ");" + GX_LF +' js = js + ConvertExpression(args) + " = " + vname + "[0];" +' ConvertInput = js +'End Function + +Function ConvertInput$ (m As Method, args As String) + Dim js As String + Dim vname As String + Dim pcount As Integer + ReDim parts(0) As String + ReDim vars(0) As String + Dim varIndex As Integer: varIndex = 1 + Dim preventNewline As String: preventNewline = "false" + Dim addQuestionPrompt As String: addQuestionPrompt = "false" + Dim prompt As String: prompt = "undefined" + Dim vcount As Integer + + Dim p As String + pcount = PrintSplit(args, parts()) + Dim i As Integer + For i = 1 To pcount + p = _Trim$(parts(i)) + If p = ";" Then + If i = 1 Then + preventNewline = "true" + Else + addQuestionPrompt = "true" + + End If + ElseIf StartsWith(p, Chr$(34)) Then + prompt = p + ElseIf p <> "," Then + vcount = UBound(vars) + 1 + ReDim _Preserve As String vars(vcount) + vars(vcount) = p + End If + Next i + + vname = GenJSVar '"___i" + _Trim$(Str$(_Round(Rnd * 10000000))) + js = "var " + vname + " = new Array(" + Str$(UBound(vars)) + ");" + GX_LF + js = js + m.jsname + "(" + vname + ", " + preventNewline + ", " + addQuestionPrompt + ", " + prompt + ");" + GX_LF + For i = 1 To UBound(vars) + js = js + ConvertExpression(vars(i)) + " = " + vname + "[" + Str$(i - 1) + "];" + GX_LF + Next i + ConvertInput = js +End Function + + +Function ConvertSwap$ (m As Method, args As String) + Dim js As String + Dim swapArray As String: swapArray = GenJSVar + Dim swapArgs(0) As String + Dim swapCount As Integer + swapCount = ListSplit(args, swapArgs()) + Dim var1 As String + Dim var2 As String + var1 = ConvertExpression(swapArgs(1)) + var2 = ConvertExpression(swapArgs(2)) + js = "var " + swapArray + " = [" + var1 + "," + var2 + "];" + GX_LF + js = js + m.jsname + "(" + swapArray + ");" + GX_LF + js = js + var1 + " = " + swapArray + "[0];" + GX_LF + js = js + var2 + " = " + swapArray + "[1];" + ConvertSwap = js +End Function + +Function GenJSVar$ + GenJSVar = "___v" + _Trim$(Str$(_Round(Rnd * 10000000))) +End Function + +Function FindParamChar (s As String, char As String) + Dim idx As Integer + idx = -1 + + Dim c As String + Dim quote As Integer + Dim paren As Integer + Dim i As Integer + For i = 1 To Len(s) + c = Mid$(s, i, 1) + If c = Chr$(34) Then + quote = Not quote + ElseIf Not quote And c = "(" Then + paren = paren + 1 + ElseIf Not quote And c = ")" Then + paren = paren - 1 + ElseIf Not quote And paren = 0 And c = char Then + idx = i + Exit For + End If + Next i + + FindParamChar = idx +End Function + +Function DeclareVar$ (parts() As String) + + Dim vname As String + Dim vtype As String: vtype = "" + Dim vtypeIndex As Integer: vtypeIndex = 4 + Dim isGlobal As Integer: isGlobal = False + Dim isArray As Integer: isArray = False + Dim arraySize As String + Dim pstart As Integer + Dim bvar As Variable + Dim varnames(0) As String + Dim vnamecount As Integer + Dim findVar As Variable + Dim asIdx As Integer + asIdx = 0 + Dim js As String: js = "" + Dim preserve As String: preserve = "false" + + Dim i As Integer + For i = 1 To UBound(parts) + If UCase$(parts(i)) = "AS" Then asIdx = i + If UCase$(parts(i)) = "_PRESERVE" Then preserve = "true" + If UCase$(parts(i)) = "SHARED" Then isGlobal = True + Next i + + + If asIdx = 2 Or _ + (asIdx = 3 And (isGlobal Or preserve = "true")) Or _ + (asIdx = 4 And isGlobal And preserve = "true") Then + ' Handle Dim As syntax + bvar.type = UCase$(parts(asIdx + 1)) + Dim nextIdx As Integer + nextIdx = asIdx + 2 + If bvar.type = "_UNSIGNED" Then + bvar.type = bvar.type + " " + UCase$(parts(asIdx + 2)) + nextIdx = asIdx + 3 + End If + bvar.typeId = FindTypeId(bvar.type) + + vnamecount = ListSplit(Join(parts(), nextIdx, -1, " "), varnames()) + For i = 1 To vnamecount + vname = _Trim$(varnames(i)) + pstart = InStr(vname, "(") + If pstart > 0 Then + bvar.isArray = True + arraySize = ConvertExpression(Mid$(vname, pstart + 1, Len(vname) - pstart - 1)) + bvar.name = RemoveSuffix(Left$(vname, pstart - 1)) + Else + bvar.isArray = False + arraySize = "" + bvar.name = vname + End If + bvar.jsname = "" + + ' TODO: this code is in two places - refactor into a separate function + If Not bvar.isArray Then + js = js + "var " + bvar.name + " = " + InitTypeValue(bvar.type) + ";" + + Else + If FindVariable(bvar.name, findVar, True) Then + js = js + "QB.resizeArray(" + bvar.name + ", [" + arraySize + "], " + InitTypeValue(bvar.type) + ", " + preserve + ");" + Else + js = js + "var " + bvar.name + " = QB.initArray([" + arraySize + "], " + InitTypeValue(bvar.type) + ");" + End If + End If + + If isGlobal Then + AddVariable bvar, globalVars() + Else + AddVariable bvar, localVars() + End If + + js = js + " // " + bvar.type + + If i < vnamecount Then js = js + GX_LF + Next i + + + Else + Dim vpartcount As Integer + Dim vparts(0) As String + nextIdx = 0 + For i = 1 To UBound(parts) + Dim p As String + p = UCase$(parts(i)) + If p = "DIM" Or p = "REDIM" Or p = "SHARED" Or p = "_PRESERVE" Then + nextIdx = i + 1 + End If + Next i + + vnamecount = ListSplit(Join(parts(), nextIdx, -1, " "), varnames()) + For i = 1 To vnamecount + + 'Handle traditional syntax + + ''If UCase$(parts(2)) = "SHARED" Then + '' isGlobal = True + '' vname = Join(parts(), 3, asIdx - 1, " ") + '' vtype = "" + '' vtypeIndex = 5 + ''Else + '' vname = Join(parts(), 2, asIdx - 1, " ") + '' vtype = "" + ''End If + vpartcount = SLSplit(varnames(i), vparts()) + bvar.name = RemoveSuffix(vparts(1)) + If vpartcount = 1 Then + bvar.type = DataTypeFromName(bvar.name) + ElseIf vpartcount = 3 Then + bvar.type = UCase$(vparts(3)) + ElseIf vpartcount = 4 Then + bvar.type = UCase$(Join(vparts(), 3, -1, " ")) + Else + ' Log error? + End If + bvar.typeId = FindTypeId(bvar.type) + + ''pstart = InStr(vname, "(") + ''If pstart > 0 Then + '' isArray = True + '' arraySize = ConvertExpression(Mid$(vname, pstart + 1, Len(vname) - pstart - 2)) + '' vname = Left$(vname, pstart - 1) + ''End If + + pstart = InStr(bvar.name, "(") + If pstart > 0 Then + bvar.isArray = True + arraySize = ConvertExpression(Mid$(bvar.name, pstart + 1, Len(bvar.name) - pstart - 1)) + bvar.name = RemoveSuffix(Left$(bvar.name, pstart - 1)) + Else + bvar.isArray = False + arraySize = "" + 'bvar.name = vname + End If + bvar.jsname = "" + + + ''If UBound(parts) = vtypeIndex Then + '' vtype = UCase$(parts(vtypeIndex)) + '' If vtype = "_UNSIGNED" Then vtype = vtype + " " + UCase$(parts(vtypeIndex)) + ''Else + '' vtype = DataTypeFromName(vname) + ''End If + + ' TODO: need to move this to later in the function so we can check to see whether + ' the variable has already been defined, this is particulary important + ' for handling REDIM _PRESERVE scenarios + ''bvar.name = RemoveSuffix(vname) + ''bvar.type = vtype + ''bvar.isArray = isArray + ''bvar.typeId = FindTypeId(bvar.type) + ''bvar.jsname = "" + '''var.arraySize = arraySize + ''If isGlobal Then + '' AddVariable bvar, globalVars() + ''Else + '' AddVariable bvar, localVars() + ''End If + + + 'Dim js As String + ''If Not bvar.isArray Then + '' js = "var " + bvar.name + " = " + InitTypeValue(bvar.type) + ";" + + ''Else + '' ' TODO: if this is a REDIM, make sure we are not declaring the variable twice + '' ' if this is an array with _PRESERVE specified, then enlarge or shrink the existing array + '' 'js = "var " + var.name + " = [];" 'new Array(" + Str$(var.arraySize + 1) + ");" + '' 'If arraySize <> "" Then + '' ' js = js + " QB.initArray(" + var.name + ", [" + arraySize + "], " + InitTypeValue(var.type) + ");" + '' 'End If + '' js = js + "var " + bvar.name + " = QB.initArray([" + arraySize + "], " + InitTypeValue(bvar.type) + ");" + ''End If + + ''js = js + " // " + bvar.type + ' TODO: this code is in two places - refactor into a separate function + If Not bvar.isArray Then + js = js + "var " + bvar.name + " = " + InitTypeValue(bvar.type) + ";" + + Else + If FindVariable(bvar.name, findVar, True) Then + js = js + "QB.resizeArray(" + bvar.name + ", [" + arraySize + "], " + InitTypeValue(bvar.type) + ", " + preserve + ");" + Else + js = js + "var " + bvar.name + " = QB.initArray([" + arraySize + "], " + InitTypeValue(bvar.type) + ");" + End If + End If + + If isGlobal Then + AddVariable bvar, globalVars() + Else + AddVariable bvar, localVars() + End If + + js = js + " // " + bvar.type + + If i < vnamecount Then js = js + GX_LF + Next i + End If + + DeclareVar = js +End Function + +Function InitTypeValue$ (vtype As String) + Dim value As String + If vtype = "STRING" Then + value = "''" + ElseIf vtype = "_BIT" Or vtype = "_UNSIGNED _BIT" Or vtype = "_BYTE" Or vtype = "_UNSIGNED _BYTE" Or _ + vtype = "INTEGER" Or vtype = "_UNSIGNED INTEGER" Or vtype = "LONG" Or vtype = "_UNSIGNED LONG" Or _ + vtype = "_INTEGER64" Or vtype = "_UNSIGNED INTEGER64" Or _ + vtype = "SINGLE" Or vtype = "DOUBLE" Or vtype = "_FLOAT" Or _ + vtype = "_OFFSET" Or vtype = "_UNSIGNED _OFFSET" Then + value = "0" + Else ' Custom Type + value = "{" + Dim typeId As Integer + typeId = FindTypeId(vtype) + Dim i As Integer + For i = 1 To UBound(typeVars) + If typeId = typeVars(i).typeId Then + value = value + typeVars(i).name + ":" + InitTypeValue(typeVars(i).type) + "," + End If + Next i + value = Left$(value, Len(value) - 1) + "}" + End If + + InitTypeValue = value +End Function + +Function FindTypeId (typeName As String) + Dim id As Integer + id = -1 + Dim i As Integer + For i = 1 To UBound(types) + If types(i).name = typeName Then + id = i + Exit For + End If + Next i + FindTypeId = id +End Function + +Function ConvertExpression$ (ex As String) + Dim c As String + Dim js As String: js = "" + Dim word As String: word = "" + Dim bvar As Variable + Dim m As Method + + Dim stringLiteral As Integer + Dim i As Integer: i = 1 + While i <= Len(ex) + c = Mid$(ex, i, 1) + + If c = Chr$(34) Then + js = js + c + stringLiteral = Not stringLiteral + + ElseIf stringLiteral Then + js = js + c + + Else + If c = " " Or c = "," Or i = Len(ex) Then + If i = Len(ex) Then word = word + c + Dim uword As String: uword = UCase$(word) + If uword = "NOT" Then + js = js + "!" + ElseIf uword = "AND" Then + js = js + " && " + ElseIf uword = "OR" Then + js = js + " || " + ElseIf uword = "MOD" Then + js = js + " % " + ElseIf word = "=" Then + js = js + " == " + ElseIf word = "<>" Then + js = js + " != " + ElseIf word = "^" Then + js = js + " ** " + ElseIf word = ">" Or word = ">=" Or word = "<" Or word = "<=" Then + js = js + " " + word + " " + Else + If FindVariable(word, bvar, False) Then + js = js + " " + bvar.jsname + Else + ' TODO: Need a more sophisticated way to determine whether + ' the return value is being assigned in the method. + ' Currently, this does not support recursive calls. + If FindMethod(word, m, "FUNCTION") Then + If m.name <> currentMethod Then + js = js + " " + m.jsname + "()" + Else + js = js + " " + word + End If + Else + js = js + " " + word + End If + + End If + End If + If c = "," And i <> Len(ex) Then js = js + "," + word = "" + + ElseIf c = "(" Then + ' Find the end of the group + Dim done As Integer: done = False + Dim pcount As Integer: pcount = 0 + Dim c2 As String + Dim ex2 As String: ex2 = "" + Dim stringLiteral2 As Integer + stringLiteral2 = False + i = i + 1 + While Not done And i <= Len(ex) + c2 = Mid$(ex, i, 1) + If c2 = Chr$(34) Then + stringLiteral2 = Not stringLiteral2 + ElseIf Not stringLiteral2 And c2 = "(" Then + pcount = pcount + 1 + ElseIf Not stringLiteral2 And c2 = ")" Then + If pcount = 0 Then + done = True + Else + pcount = pcount - 1 + End If + End If + + If Not done Then + ex2 = ex2 + c2 + i = i + 1 + End If + Wend + + ' Determine whether the current word is a function or array variable + Dim fneg As String + fneg = "" + If Len(word) > 0 Then + If Left$(word, 1) = "-" Then + fneg = "-" + word = Mid$(word, 2) + End If + End If + If FindVariable(word, bvar, True) Then + If _Trim$(ex2) = "" Then + ' This is the case where the array variable is being passed as a parameter + js = js + fneg + bvar.jsname + Else + ' This is the case where a dimension is specified in order to retrieve or set a value in the array + js = js + fneg + "QB.arrayValue(" + bvar.jsname + ", [" + ConvertExpression(ex2) + "]).value" + 'If bvar.typeId < 1 Then js = js + ".value" + End If + ElseIf FindMethod(word, m, "FUNCTION") Then + js = js + fneg + m.jsname + "(" + ConvertExpression(ex2) + ")" + Else + 'If _Trim$(word) <> "" Then AddJSLine 0, "//// MISSING FUNCTION? [" + word + "]" '*Print "//// MISSING FUNCTION? [" + word + "]" + If _Trim$(word) <> "" Then AddWarning i, "Missing function or array [" + word + "]" + ' nested condition + js = js + fneg + "(" + ConvertExpression(ex2) + ")" + End If + word = "" + + Else + word = word + c + End If + End If + i = i + 1 + Wend + ConvertExpression = js +End Function + +Function FindVariable (varname As String, bvar As Variable, isArray As Integer) + Dim found As Integer: found = False + Dim i As Integer + Dim fvarname As String + fvarname = _Trim$(UCase$(RemoveSuffix(varname))) + For i = 1 To UBound(localVars) + If localVars(i).isArray = isArray And UCase$(localVars(i).name) = fvarname Then + found = True + 'bvar = localVars(i) + ' TODO: This is a work around for the pass by reference issue. + ' Once corrected, the code above can be used instead + bvar.type = localVars(i).type + bvar.name = localVars(i).name + bvar.jsname = localVars(i).jsname + bvar.isConst = localVars(i).isConst + bvar.isArray = localVars(i).isArray + bvar.arraySize = localVars(i).arraySize + bvar.typeId = localVars(i).typeId + Exit For + End If + Next i + If Not found Then + For i = 1 To UBound(globalVars) + If globalVars(i).isArray = isArray And UCase$(globalVars(i).name) = fvarname Then + found = True + 'bvar = globalVars(i) + ' TODO: This is a work around for the pass by reference issue. + ' Once corrected, the code above can be used instead + bvar.type = globalVars(i).type + bvar.name = globalVars(i).name + bvar.jsname = globalVars(i).jsname + bvar.isConst = globalVars(i).isConst + bvar.isArray = globalVars(i).isArray + bvar.arraySize = globalVars(i).arraySize + bvar.typeId = globalVars(i).typeId + Exit For + End If + Next i + End If + + FindVariable = found +End Function + +Function FindMethod (mname As String, m As Method, t As String) + Dim found As Integer: found = False + Dim i As Integer + For i = 1 To UBound(methods) + If methods(i).uname = _Trim$(UCase$(RemoveSuffix(mname))) And methods(i).type = t Then + found = True + 'm = methods(i) + ' TODO: This is a work around for the pass by reference issue. + ' Once corrected, the code above can be used instead + m.line = methods(i).line + m.type = methods(i).type + m.returnType = methods(i).returnType + m.name = methods(i).name + m.jsname = methods(i).jsname + m.uname = methods(i).uname + m.argc = methods(i).argc + m.args = methods(i).args + Exit For + End If + Next i + FindMethod = found +End Function + +Sub ConvertMethods () + AddJSLine 0, "" + Dim i As Integer + For i = 1 To UBound(methods) + If (methods(i).line <> 0) Then + Dim lastLine As Integer + lastLine = methods(i + 1).line - 1 + If lastLine < 0 Then lastLine = UBound(lines) + + ' clear the local variables + ReDim As Variable localVars(0) + + ' TODO: figure out how to make needed functions have the async modifier + ' at the moment just applying it to all subs + Dim asyncModifier As String + If methods(i).type = "SUB" Then + asyncModifier = "async " + Else + asyncModifier = "" + End If + Dim methodDec As String + methodDec = asyncModifier + "function " + methods(i).jsname + "(" + If methods(i).argc > 0 Then + ReDim As String args(0) + Dim c As Integer + c = Split(methods(i).args, ",", args()) + Dim a As Integer + For a = 1 To c + Dim v As Integer + ReDim As String parts(0) + v = Split(args(a), ":", parts()) + methodDec = methodDec + parts(1) + "/*" + parts(2) + "*/" + If a < c Then methodDec = methodDec + "," + + ' add the parameter to the local variables + Dim bvar As Variable + bvar.name = parts(1) + bvar.type = parts(2) + bvar.typeId = FindTypeId(bvar.type) + If parts(3) = "true" Then + bvar.isArray = True + End If + bvar.jsname = "" + AddVariable bvar, localVars() + + Next a + End If + methodDec = methodDec + ") {" + AddJSLine methods(i).line, methodDec + AddJSLine methods(i).line, "if (QB.halted()) { return; }" + If methods(i).type = "FUNCTION" Then + AddJSLine methods(i).line, "var " + RemoveSuffix(methods(i).name) + " = null;" + End If + currentMethod = methods(i).name + + + ConvertLines methods(i).line + 1, lastLine - 1, methods(i).name + If methods(i).type = "FUNCTION" Then + AddJSLine lastLine, "return " + RemoveSuffix(methods(i).name) + ";" + End If + AddJSLine lastLine, "}" + End If + Next i +End Sub + + +Sub ReadLinesFromFile (filename As String) + Dim fline As String + Dim lineIndex As Integer + Open filename For Input As #1 + Do Until EOF(1) + Line Input #1, fline + lineIndex = lineIndex + 1 + + If _Trim$(fline) <> "" Then ' remove all blank lines + + While EndsWith(fline, " _") + Dim nextLine As String + Line Input #1, nextLine + fline = Left$(fline, Len(fline) - 1) + nextLine + Wend + + ReadLine lineIndex, fline + End If + Loop + Close #1 +End Sub + +Sub ReadLinesFromText (sourceText As String) + ReDim As String sourceLines(0) + Dim lcount As Integer + Dim i As Integer + lcount = Split(sourceText, GX_LF, sourceLines()) + For i = 1 To lcount + Dim fline As String + fline = sourceLines(i) + + If _Trim$(fline) <> "" Then ' remove all blank lines + + Dim lineIndex As Integer + lineIndex = i + + While EndsWith(fline, "_") + i = i + 1 + Dim nextLine As String + nextLine = sourceLines(i) + fline = Left$(fline, Len(fline) - 1) + nextLine + Wend + + ReadLine i, fline + End If + Next i +End Sub + +Sub ReadLine (lineIndex As Integer, fline As String) + Dim quoteDepth As Integer + quoteDepth = 0 + Dim i As Integer + For i = 1 To Len(fline) + Dim c As String + c = Mid$(fline, i, 1) + If c = Chr$(34) Then + If quoteDepth = 0 Then + quoteDepth = 1 + Else + quoteDepth = 0 + End If + End If + If quoteDepth = 0 And c = "'" Then + fline = Left$(fline, i - 1) + Exit For + End If + If quoteDepth = 0 And c = ":" Then + AddLine lineIndex, Left$(fline, i - 1) + fline = Right$(fline, Len(fline) - i) + i = 0 + End If + Next i + + ' If once we have removed the comments the line is empty do not add it + If _Trim$(fline) <> "" Then + AddLine lineIndex, fline + End If +End Sub + +Sub FindMethods + Dim i As Integer + Dim pcount As Integer + ReDim As String parts(0) + For i = 1 To UBound(lines) + pcount = Split(lines(i).text, " ", parts()) + Dim word As String: word = UCase$(parts(1)) + + If word = "FUNCTION" Or word = "SUB" Then + + Dim m As Method + m.line = i + m.type = UCase$(parts(1)) + m.name = parts(2) + m.argc = 0 + m.args = "" + ReDim As Argument args(0) + + If UBound(parts) > 2 Then + Dim a As Integer + Dim args As String + args = "" + For a = 3 To UBound(parts) + args = args + parts(a) + " " + Next a + 'Print "---> " + args + 'args = _Trim$(GXSTR_Replace(GXSTR_Replace(args, "(", ""), ")", "")) + args = Mid$(_Trim$(args), 2, Len(_Trim$(args)) - 2) + 'Print "---< " + args + ReDim As String arga(0) + 'm.argc = GXSTR_Split(args, ",", arga()) + m.argc = ListSplit(args, arga()) + args = "" + For a = 1 To m.argc + 'Dim arg As String + ReDim As String aparts(0) + Dim apcount As Integer + Dim argname As String + Dim isArray As String: isArray = "false" + apcount = Split(arga(a), " ", aparts()) + argname = aparts(1) + 'Print "---: " + argname + If EndsWith(argname, "()") Then + isArray = "true" + argname = Left$(argname, Len(argname) - 2) + End If + If apcount = 3 Then + 'args = args + aparts(1) + ":" + UCase$(aparts(3)) + args = args + argname + ":" + UCase$(aparts(3)) + ":" + isArray + Else + 'args = args + aparts(1) + ":" + DataTypeFromName(aparts(1)) + args = args + argname + ":" + DataTypeFromName(aparts(1)) + ":" + isArray + End If + If a <> m.argc Then + args = args + "," + End If + Next a + m.args = args + End If + + AddMethod m, "" + End If + Next i +End Sub + +' TODO: look at refactoring this - do we really need 3 different variations of a split function? +Function Split (sourceString As String, delimiter As String, results() As String) + ' Modified version of: + ' https://www.qb64.org/forum/index.php?topic=1073.msg102711#msg102711 + Dim cstr As String + Dim As Long p, curpos, arrpos, dpos + + ' Make a copy of the source string + cstr = sourceString + + ' Special case if the delimiter is space, remove all excess space + If delimiter = " " Then + cstr = RTrim$(LTrim$(cstr)) + p = InStr(cstr, " ") + While p > 0 + cstr = Mid$(cstr, 1, p - 1) + Mid$(cstr, p + 1) + p = InStr(cstr, " ") + Wend + End If + curpos = 1 + arrpos = 0 + dpos = InStr(curpos, cstr, delimiter) + Do Until dpos = 0 + arrpos = arrpos + 1 + ReDim _Preserve As String results(arrpos) + results(arrpos) = Mid$(cstr, curpos, dpos - curpos) + curpos = dpos + Len(delimiter) + dpos = InStr(curpos, cstr, delimiter) + Loop + arrpos = arrpos + 1 + ReDim _Preserve As String results(arrpos) + results(arrpos) = Mid$(cstr, curpos) + + Split = arrpos +End Function + + +' String literal-aware split +Function SLSplit (sourceString As String, results() As String) + Dim cstr As String + Dim As Long p, curpos, arrpos, dpos + + cstr = _Trim$(sourceString) + + ReDim As String results(0) + + Dim lastChar As String + Dim quoteMode As Integer + Dim result As String + Dim count As Integer + Dim i As Integer + For i = 1 To Len(cstr) + Dim c As String + c = Mid$(cstr, i, 1) + + If c = Chr$(34) Then + quoteMode = Not quoteMode + result = result + c + + ' This is not the most intuitive place for this... + ' If we find a string then escape any backslashes + If Not quoteMode Then + result = GXSTR_Replace(result, "\", "\\") + End If + + ElseIf c = " " Then + If quoteMode Then + result = result + c + + ElseIf lastChar = " " Then + ' extra space, move along + + Else + count = UBound(results) + 1 + ReDim _Preserve As String results(count) + results(count) = result + result = "" + End If + Else + result = result + c + End If + + lastChar = c + Next i + + ' add the leftover last segment + If result <> "" Then + count = UBound(results) + 1 + ReDim _Preserve As String results(count) + results(count) = result + End If + + SLSplit = UBound(results) +End Function + +Function ListSplit (sourceString As String, results() As String) + Dim cstr As String + Dim As Long p, curpos, arrpos, dpos + + cstr = _Trim$(sourceString) + + ReDim As String results(0) + + Dim quoteMode As Integer + Dim result As String + Dim count As Integer + Dim paren As Integer + Dim i As Integer + For i = 1 To Len(cstr) + Dim c As String + c = Mid$(cstr, i, 1) + + If c = Chr$(34) Then + quoteMode = Not quoteMode + result = result + c + + ElseIf quoteMode Then + result = result + c + + ElseIf c = "(" Then + paren = paren + 1 + result = result + c + + ElseIf c = ")" Then + paren = paren - 1 + result = result + c + + ElseIf paren > 0 Then + result = result + c + + ElseIf c = "," Then + + count = UBound(results) + 1 + ReDim _Preserve As String results(count) + results(count) = result + result = "" + Else + result = result + c + End If + + Next i + + ' add the leftover last segment + If result <> "" Then + count = UBound(results) + 1 + ReDim _Preserve As String results(count) + results(count) = result + End If + + ListSplit = UBound(results) +End Function + +' TODO: This copy and paste approach has gotten completely out of hand. +' I need to just bite the bullet and really implement a genericized +' version that can be used for multiple scenarios +Function PrintSplit (sourceString As String, results() As String) + Dim cstr As String + Dim As Long p, curpos, arrpos, dpos + + cstr = _Trim$(sourceString) + + ReDim As String results(0) + + Dim quoteMode As Integer + Dim result As String + Dim count As Integer + Dim paren As Integer + Dim i As Integer + For i = 1 To Len(cstr) + Dim c As String + c = Mid$(cstr, i, 1) + + If c = Chr$(34) Then + quoteMode = Not quoteMode + result = result + c + + ElseIf quoteMode Then + result = result + c + + ElseIf c = "(" Then + paren = paren + 1 + result = result + c + + ElseIf c = ")" Then + paren = paren - 1 + result = result + c + + ElseIf paren > 0 Then + result = result + c + + ElseIf c = "," Or c = ";" Then + ' add the previous expression + If result <> "" Then + count = UBound(results) + 1 + ReDim _Preserve As String results(count) + results(count) = result + result = "" + End If + + ' add the delimiter too + count = UBound(results) + 1 + ReDim _Preserve As String results(count) + results(count) = c + Else + result = result + c + End If + + Next i + + ' add the leftover last segment + If result <> "" Then + count = UBound(results) + 1 + ReDim _Preserve As String results(count) + results(count) = result + End If + + PrintSplit = UBound(results) +End Function + + + +Sub PrintMethods + Print "" + Print "Methods" + Print "------------------------------------------------------------" + Dim i As Integer + For i = 1 To UBound(methods) + Dim m As Method + m = methods(i) + Print Str$(m.line) + ": " + m.type + " - " + m.name + " [" + m.jsname + "] - " + m.returnType + " - " + m.args + Next i +End Sub + +Sub PrintTypes + Print "" + Print "Types" + Print "------------------------------------------------------------" + Dim i As Integer + For i = 1 To UBound(types) + Dim t As QBType + t = types(i) + Print Str$(t.line) + ": " + t.name ' + " - " + m.args + Dim v As Integer + For v = 1 To UBound(typeVars) + If typeVars(i).typeId = i Then + Print " -> " + typeVars(v).name + ": " + typeVars(v).type + End If + Next v + Next i +End Sub + + +Sub AddMethod (m As Method, prefix As String) + Dim mcount: mcount = UBound(methods) + 1 + ReDim _Preserve As Method methods(mcount) + If m.type = "FUNCTION" Then + m.returnType = DataTypeFromName(m.name) + End If + m.uname = UCase$(RemoveSuffix(m.name)) + m.jsname = MethodJS(m, prefix) + methods(mcount) = m +End Sub + +Sub AddGXMethod (mtype As String, mname As String) + Dim mcount: mcount = UBound(methods) + 1 + ReDim _Preserve As Method methods(mcount) + Dim m As Method + m.type = mtype + m.name = mname + m.uname = UCase$(m.name) + m.jsname = GXMethodJS(RemoveSuffix(mname)) + If mtype = "FUNCTION" Then + m.returnType = DataTypeFromName(mname) + End If + methods(mcount) = m +End Sub + +Sub AddQBMethod (mtype As String, mname As String) + Dim m As Method + m.type = mtype + m.name = mname + AddMethod m, "QB." +End Sub + + +Sub AddLine (lineIndex As Integer, fline As String) + ' check for single line if statements + Dim parts(0) As String + Dim c As Integer + c = Split(fline, " ", parts()) + + If UCase$(parts(1)) = "IF" Then + Dim thenIndex As Integer + thenIndex = 0 + Dim i As Integer + For i = 1 To c + If UCase$(parts(i)) = "THEN" Then + thenIndex = i + Exit For + End If + Next i + + If thenIndex <> c Then + __AddLine lineIndex, Join(parts(), 1, thenIndex, " ") + __AddLine lineIndex, Join(parts(), thenIndex + 1, c, " ") + __AddLine lineIndex, "End If" + Else + __AddLine lineIndex, fline + End If + Else + __AddLine lineIndex, fline + End If +End Sub + +Sub __AddLine (lineIndex As Integer, fline As String) + Dim lcount As Integer: lcount = UBound(lines) + 1 + ReDim _Preserve As CodeLine lines(lcount) + 'Dim cline As CodeLine + 'cline.line = lineIndex + 'cline.text = fline + 'lines(lcount) = cline + lines(lcount).line = lineIndex + lines(lcount).text = fline +End Sub + +Sub AddJSLine (sourceLine As Integer, jsline As String) + Dim lcount As Integer: lcount = UBound(jsLines) + 1 + ReDim _Preserve As CodeLine jsLines(lcount) + 'Dim cline As CodeLine + 'cline.line = sourceLine + 'cline.text = jsline + 'jsLines(lcount) = cline + jsLines(lcount).line = sourceLine + jsLines(lcount).text = jsline +End Sub + +Sub AddWarning (sourceLine As Integer, msgText As String) + Dim lcount As Integer: lcount = UBound(warnings) + 1 + ReDim _Preserve As CodeLine warnings(lcount) + Dim l As Integer + If (sourceLine > 0) Then + l = lines(sourceLine).line + End If + + warnings(lcount).line = l + warnings(lcount).text = msgText +End Sub + + +Sub AddConst (vname As String) + Dim v As Variable + v.type = "CONST" + v.name = vname + v.isConst = True + AddVariable v, globalVars() +End Sub + +Sub AddGXConst (vname As String) + Dim v As Variable + v.type = "CONST" + v.name = vname + If vname = "GX_TRUE" Then + v.jsname = "true" + ElseIf vname = "GX_FALSE" Then + v.jsname = "false" + Else + Dim jsname As String + jsname = Mid$(vname, 3, Len(vname) - 2) + If Left$(jsname, 1) = "_" Then jsname = Right$(jsname, Len(jsname) - 1) + v.jsname = "GX." + jsname + End If + v.isConst = True + AddVariable v, globalVars() +End Sub + +Sub AddGlobal (vname As String, vtype As String, arraySize As Integer) + Dim v As Variable + v.type = vtype + v.name = vname + v.isArray = arraySize > -1 + v.arraySize = arraySize + AddVariable v, globalVars() +End Sub + +Sub AddLocal (vname As String, vtype As String, arraySize As Integer) + Dim v As Variable + v.type = vtype + v.name = vname + v.isArray = arraySize > -1 + v.arraySize = arraySize + AddVariable v, localVars() +End Sub + +Sub AddVariable (bvar As Variable, vlist() As Variable) + Dim vcount: vcount = UBound(vlist) + 1 + ReDim _Preserve As Variable vlist(vcount) + If bvar.jsname = "" Then bvar.jsname = RemoveSuffix(bvar.name) + vlist(vcount) = bvar +End Sub + +Sub AddType (t As QBType) + Dim tcount: tcount = UBound(types) + 1 + ReDim _Preserve As QBType types(tcount) + types(tcount) = t +End Sub + +Sub AddSystemType (tname As String, args As String) + Dim t As QBType + t.name = tname + 't.argc = argc + 't.args = args + AddType t + Dim typeId As Integer + typeId = UBound(types) + Dim count As Integer + ReDim As String pairs(0) + count = Split(args, ",", pairs()) + Dim i As Integer + For i = 1 To UBound(pairs) + ReDim As String nv(0) + count = Split(pairs(i), ":", nv()) + Dim tvar As Variable + tvar.typeId = typeId + tvar.name = nv(1) + tvar.type = UCase$(nv(2)) + AddVariable tvar, typeVars() + Next i +End Sub + +Function MainEnd + If programMethods = 0 Then + MainEnd = UBound(lines) + Else + MainEnd = methods(1).line - 1 + End If +End Function + +Function RemoveSuffix$ (vname As String) + Dim i As Integer + Dim done As Integer + Dim c As String + vname = _Trim$(vname) + i = Len(vname) + While Not done + c = Mid$(vname, i, 1) + If c = "`" Or c = "%" Or c = "&" Or c = "$" Or c = "~" Or c = "!" Then + i = i - 1 + Else + done = True + End If + Wend + RemoveSuffix = Left$(vname, i) +End Function + +Function DataTypeFromName$ (vname As String) + Dim dt As String + If EndsWith(vname, "$") Then + dt = "STRING" + ElseIf EndsWith(vname, "`") Then + dt = "_BIT" + ElseIf EndsWith(vname, "%%") Then + dt = "_BYTE" + ElseIf EndsWith(vname, "~%") Then + dt = "_UNSIGNED INTEGER" + ElseIf EndsWith(vname, "%") Then + dt = "INTEGER" + ElseIf EndsWith(vname, "~&&") Then + dt = "_UNSIGNED INTEGER64" + ElseIf EndsWith(vname, "&&") Then + dt = "_INTEGER64" + ElseIf EndsWith(vname, "~&") Then + dt = "_UNSIGNED LONG" + ElseIf EndsWith(vname, "##") Then + dt = "_FLOAT" + ElseIf EndsWith(vname, "#") Then + dt = "DOUBLE" + ElseIf EndsWith(vname, "~%&") Then + dt = "_UNSIGNED _OFFSET" + ElseIf EndsWith(vname, "%&") Then + dt = "_OFFSET" + ElseIf EndsWith(vname, "&") Then + dt = "LONG" + ElseIf EndsWith(vname, "!") Then + dt = "SINGLE" + Else + dt = "SINGLE" + End If + + DataTypeFromName = dt +End Function + +Function EndsWith (s As String, finds As String) + If Len(finds) > Len(s) Then + EndsWith = False + Exit Function + End If + If _InStrRev(s, finds) = Len(s) - (Len(finds) - 1) Then + EndsWith = True + Else + EndsWith = False + End If +End Function + +Function StartsWith (s As String, finds As String) + If Len(finds) > Len(s) Then + StartsWith = False + Exit Function + End If + If InStr(s, finds) = 1 Then + StartsWith = True + Else + StartsWith = False + End If +End Function + +Function Join$ (parts() As String, startIndex As Integer, endIndex As Integer, delimiter As String) + + If endIndex = -1 Then endIndex = UBound(parts) + Dim s As String + Dim i As Integer + For i = startIndex To endIndex + s = s + parts(i) + If i <> UBound(parts) Then + s = s + delimiter + End If + Next i + Join = s +End Function + +Function MethodJS$ (m As Method, prefix As String) + Dim jsname As String + jsname = prefix + If m.type = "FUNCTION" Then + jsname = jsname + "func_" + Else + jsname = jsname + "sub_" + End If + + Dim i As Integer + Dim c As String + Dim a As Integer + For i = 1 To Len(m.name) + c = Mid$(m.name, i, 1) + a = Asc(c) + ' uppercase, lowercase, numbers, - and . + If (a >= 65 And a <= 90) Or (a >= 97 And a <= 122) Or _ + (a >= 48 And a <= 57) Or _ + a = 95 Or a = 46 Then + jsname = jsname + c + End If + Next i + + If m.name = "_Limit" Or m.name = "_Delay" Or m.name = "Sleep" Or m.name = "Input" Or m.name = "Print" Or m.name = "Fetch" Then + jsname = "await " + jsname + End If + + MethodJS = jsname +End Function + +Function GXMethodJS$ (mname As String) + Dim jsname As String + Dim startIdx As Integer + If InStr(mname, "GXSTR") = 1 Then + jsname = "GXSTR." + startIdx = 7 + Else + jsname = "GX." + startIdx = 3 + End If + jsname = jsname + LCase$(Mid$(mname, startIdx, 1)) + + Dim i As Integer + Dim c As String + Dim a As Integer + For i = startIdx + 1 To Len(mname) + c = Mid$(mname, i, 1) + a = Asc(c) + ' uppercase, lowercase, numbers, - and . + If (a >= 65 And a <= 90) Or (a >= 97 And a <= 122) Or _ + (a >= 48 And a <= 57) Or _ + a = 95 Or a = 46 Then + jsname = jsname + c + End If + Next i + + If mname = "GXMapLoad" Or mname = "GXSceneStart" Then + jsname = "await " + jsname + End If + + GXMethodJS = jsname +End Function + +Sub InitGX + AddSystemType "GXPOSITION", "x:LONG,y:LONG" + AddSystemType "GXDEVICEINPUT", "deviceId:INTEGER,deviceType:INTEGER,inputType:INTEGER,inputId:INTEGER,inputValue:INTEGER" + + + AddGXConst "GX_FALSE" + AddGXConst "GX_TRUE" + AddGXConst "GXEVENT_INIT" + AddGXConst "GXEVENT_UPDATE" + AddGXConst "GXEVENT_DRAWBG" + AddGXConst "GXEVENT_DRAWMAP" + AddGXConst "GXEVENT_DRAWSCREEN" + AddGXConst "GXEVENT_MOUSEINPUT" + AddGXConst "GXEVENT_PAINTBEFORE" + AddGXConst "GXEVENT_PAINTAFTER" + AddGXConst "GXEVENT_COLLISION_TILE" + AddGXConst "GXEVENT_COLLISION_ENTITY" + AddGXConst "GXEVENT_PLAYER_ACTION" + AddGXConst "GXEVENT_ANIMATE_COMPLETE" + AddGXConst "GXANIMATE_LOOP" + AddGXConst "GXANIMATE_SINGLE" + AddGXConst "GXBG_STRETCH" + AddGXConst "GXBG_SCROLL" + AddGXConst "GXBG_WRAP" + AddGXConst "GXKEY_ESC" + AddGXConst "GXKEY_1" + AddGXConst "GXKEY_2" + AddGXConst "GXKEY_3" + AddGXConst "GXKEY_4" + AddGXConst "GXKEY_5" + AddGXConst "GXKEY_6" + AddGXConst "GXKEY_7" + AddGXConst "GXKEY_8" + AddGXConst "GXKEY_9" + AddGXConst "GXKEY_0" + AddGXConst "GXKEY_DASH" + AddGXConst "GXKEY_EQUALS" + AddGXConst "GXKEY_BACKSPACE" + AddGXConst "GXKEY_TAB" + AddGXConst "GXKEY_Q" + AddGXConst "GXKEY_W" + AddGXConst "GXKEY_E" + AddGXConst "GXKEY_R" + AddGXConst "GXKEY_T" + AddGXConst "GXKEY_Y" + AddGXConst "GXKEY_U" + AddGXConst "GXKEY_I" + AddGXConst "GXKEY_O" + AddGXConst "GXKEY_P" + AddGXConst "GXKEY_LBRACKET" + AddGXConst "GXKEY_RBRACKET" + AddGXConst "GXKEY_ENTER" + AddGXConst "GXKEY_LCTRL" + AddGXConst "GXKEY_A" + AddGXConst "GXKEY_S" + AddGXConst "GXKEY_D" + AddGXConst "GXKEY_F" + AddGXConst "GXKEY_G" + AddGXConst "GXKEY_H" + AddGXConst "GXKEY_J" + AddGXConst "GXKEY_K" + AddGXConst "GXKEY_L" + AddGXConst "GXKEY_SEMICOLON" + AddGXConst "GXKEY_QUOTE" + AddGXConst "GXKEY_BACKQUOTE" + AddGXConst "GXKEY_LSHIFT" + AddGXConst "GXKEY_BACKSLASH" + AddGXConst "GXKEY_Z" + AddGXConst "GXKEY_X" + AddGXConst "GXKEY_C" + AddGXConst "GXKEY_V" + AddGXConst "GXKEY_B" + AddGXConst "GXKEY_N" + AddGXConst "GXKEY_M" + AddGXConst "GXKEY_COMMA" + AddGXConst "GXKEY_PERIOD" + AddGXConst "GXKEY_SLASH" + AddGXConst "GXKEY_RSHIFT" + AddGXConst "GXKEY_NUMPAD_MULTIPLY" + AddGXConst "GXKEY_SPACEBAR" + AddGXConst "GXKEY_CAPSLOCK" + AddGXConst "GXKEY_F1" + AddGXConst "GXKEY_F2" + AddGXConst "GXKEY_F3" + AddGXConst "GXKEY_F4" + AddGXConst "GXKEY_F5" + AddGXConst "GXKEY_F6" + AddGXConst "GXKEY_F7" + AddGXConst "GXKEY_F8" + AddGXConst "GXKEY_F9" + AddGXConst "GXKEY_PAUSE" + AddGXConst "GXKEY_SCRLK" + AddGXConst "GXKEY_NUMPAD_7" + AddGXConst "GXKEY_NUMPAD_8" + AddGXConst "GXKEY_NUMPAD_9" + AddGXConst "GXKEY_NUMPAD_MINUS" + AddGXConst "GXKEY_NUMPAD_4" + AddGXConst "GXKEY_NUMPAD_5" + AddGXConst "GXKEY_NUMPAD_6" + AddGXConst "GXKEY_NUMPAD_PLUS" + AddGXConst "GXKEY_NUMPAD_1" + AddGXConst "GXKEY_NUMPAD_2" + AddGXConst "GXKEY_NUMPAD_3" + AddGXConst "GXKEY_NUMPAD_0" + AddGXConst "GXKEY_NUMPAD_PERIOD" + AddGXConst "GXKEY_F11" + AddGXConst "GXKEY_F12" + AddGXConst "GXKEY_NUMPAD_ENTER" + AddGXConst "GXKEY_RCTRL" + AddGXConst "GXKEY_NUMPAD_DIVIDE" + AddGXConst "GXKEY_NUMLOCK" + AddGXConst "GXKEY_HOME" + AddGXConst "GXKEY_UP" + AddGXConst "GXKEY_PAGEUP" + AddGXConst "GXKEY_LEFT" + AddGXConst "GXKEY_RIGHT" + AddGXConst "GXKEY_END" + AddGXConst "GXKEY_DOWN" + AddGXConst "GXKEY_PAGEDOWN" + AddGXConst "GXKEY_INSERT" + AddGXConst "GXKEY_DELETE" + AddGXConst "GXKEY_LWIN" + AddGXConst "GXKEY_RWIN" + AddGXConst "GXKEY_MENU" + AddGXConst "GXACTION_MOVE_LEFT" + AddGXConst "GXACTION_MOVE_RIGHT" + AddGXConst "GXACTION_MOVE_UP" + AddGXConst "GXACTION_MOVE_DOWN" + AddGXConst "GXACTION_JUMP" + AddGXConst "GXACTION_JUMP_RIGHT" + AddGXConst "GXACTION_JUMP_LEFT" + AddGXConst "GXSCENE_FOLLOW_NONE" + AddGXConst "GXSCENE_FOLLOW_ENTITY_CENTER" + AddGXConst "GXSCENE_FOLLOW_ENTITY_CENTER_X" + AddGXConst "GXSCENE_FOLLOW_ENTITY_CENTER_Y" + AddGXConst "GXSCENE_FOLLOW_ENTITY_CENTER_X_POS" + AddGXConst "GXSCENE_FOLLOW_ENTITY_CENTER_X_NEG" + AddGXConst "GXSCENE_CONSTRAIN_NONE" + AddGXConst "GXSCENE_CONSTRAIN_TO_MAP" + AddGXConst "GXFONT_DEFAULT" + AddGXConst "GXFONT_DEFAULT_BLACK" + AddGXConst "GXDEVICE_KEYBOARD" + AddGXConst "GXDEVICE_MOUSE" + AddGXConst "GXDEVICE_CONTROLLER" + AddGXConst "GXDEVICE_BUTTON" + AddGXConst "GXDEVICE_AXIS" + AddGXConst "GXDEVICE_WHEEL" + AddGXConst "GXTYPE_ENTITY" + AddGXConst "GXTYPE_FONT" + + AddGXMethod "SUB", "GXSleep" + AddGXMethod "FUNCTION", "GXMouseX" + AddGXMethod "FUNCTION", "GXMouseY" + AddGXMethod "FUNCTION", "GXSoundLoad" + AddGXMethod "SUB", "GXSoundPlay" + AddGXMethod "SUB", "GXSoundRepeat" + AddGXMethod "SUB", "GXSoundVolume" + AddGXMethod "SUB", "GXSoundPause" + AddGXMethod "SUB", "GXSoundStop" + AddGXMethod "SUB", "GXSoundMuted" + AddGXMethod "FUNCTION", "GXSoundMuted" + AddGXMethod "SUB", "GXEntityAnimate" + AddGXMethod "SUB", "GXEntityAnimateStop" + AddGXMethod "SUB", "GXEntityAnimateMode" + AddGXMethod "FUNCTION", "GXEntityAnimateMode" + AddGXMethod "FUNCTION", "GXScreenEntityCreate" + AddGXMethod "FUNCTION", "GXEntityCreate" + AddGXMethod "SUB", "GXEntityCreate" + AddGXMethod "SUB", "GXEntityVisible" + AddGXMethod "SUB", "GXEntityMove" + AddGXMethod "SUB", "GXEntityPos" + AddGXMethod "SUB", "GXEntityVX" + AddGXMethod "FUNCTION", "GXEntityVX" + AddGXMethod "SUB", "GXEntityVY" + AddGXMethod "FUNCTION", "GXEntityVY" + AddGXMethod "FUNCTION", "GXEntityX" + AddGXMethod "FUNCTION", "GXEntityY" + AddGXMethod "FUNCTION", "GXEntityWidth" + AddGXMethod "FUNCTION", "GXEntityHeight" + AddGXMethod "SUB", "GXEntityFrameNext" + AddGXMethod "SUB", "GXEntityFrameSet" + AddGXMethod "SUB", "GXEntityType" + AddGXMethod "FUNCTION", "GXEntityType" + AddGXMethod "FUNCTION", "GXEntityUID$" + AddGXMethod "FUNCTION", "GXFontUID$" + AddGXMethod "FUNCTION", "GX" + AddGXMethod "SUB", "GXEntityApplyGravity" + AddGXMethod "FUNCTION", "GXEntityApplyGravity" + AddGXMethod "SUB", "GXEntityCollisionOffset" + AddGXMethod "FUNCTION", "GXEntityCollisionOffsetLeft" + AddGXMethod "FUNCTION", "GXEntityCollisionOffsetTop" + AddGXMethod "FUNCTION", "GXEntityCollisionOffsetRight" + AddGXMethod "FUNCTION", "GXEntityCollisionOffsetBottom" + AddGXMethod "SUB", "GXFullScreen" + AddGXMethod "FUNCTION", "GXFullScreen" + AddGXMethod "FUNCTION", "GXBackgroundAdd" + AddGXMethod "SUB", "GXBackgroundY" + AddGXMethod "SUB", "GXBackgroundHeight" + AddGXMethod "SUB", "GXBackgroundClear" + AddGXMethod "SUB", "GXSceneEmbedded" + AddGXMethod "FUNCTION", "GXSceneEmbedded" + AddGXMethod "SUB", "GXSceneCreate" + AddGXMethod "SUB", "GXSceneWindowSize" + AddGXMethod "SUB", "GXSceneScale" + AddGXMethod "SUB", "GXSceneResize" + AddGXMethod "SUB", "GXSceneDestroy" + AddGXMethod "SUB", "GXCustomDraw" + AddGXMethod "FUNCTION", "GXCustomDraw" + AddGXMethod "SUB", "GXFrameRate" + AddGXMethod "FUNCTION", "GXFrameRate" + AddGXMethod "FUNCTION", "GXFrame" + AddGXMethod "SUB", "GXSceneDraw" + AddGXMethod "SUB", "GXSceneMove" + AddGXMethod "SUB", "GXScenePos" + AddGXMethod "FUNCTION", "GXSceneX" + AddGXMethod "FUNCTION", "GXSceneY" + AddGXMethod "FUNCTION", "GXSceneWidth" + AddGXMethod "FUNCTION", "GXSceneHeight" + AddGXMethod "FUNCTION", "GXSceneColumns" + AddGXMethod "FUNCTION", "GXSceneRows" + AddGXMethod "SUB", "GXSceneStart" + AddGXMethod "SUB", "GXSceneUpdate" + AddGXMethod "SUB", "GXSceneFollowEntity" + AddGXMethod "SUB", "GXSceneConstrain" + AddGXMethod "SUB", "GXSceneStop" + AddGXMethod "SUB", "GXMapCreate" + AddGXMethod "FUNCTION", "GXMapColumns" + AddGXMethod "FUNCTION", "GXMapRows" + AddGXMethod "FUNCTION", "GXMapLayers" + AddGXMethod "SUB", "GXMapLayerVisible" + AddGXMethod "FUNCTION", "GXMapLayerVisible" + AddGXMethod "SUB", "GXMapLayerAdd" + AddGXMethod "SUB", "GXMapLayerInsert" + AddGXMethod "SUB", "GXMapLayerRemove" + AddGXMethod "SUB", "GXMapResize" + AddGXMethod "SUB", "GXMapDraw" + AddGXMethod "SUB", "GXMapTilePosAt" + AddGXMethod "SUB", "GXMapTile" + AddGXMethod "FUNCTION", "GXMapTile" + AddGXMethod "FUNCTION", "GXMapTileDepth" + AddGXMethod "SUB", "GXMapTileAdd" + AddGXMethod "SUB", "GXMapTileRemove" + AddGXMethod "FUNCTION", "GXMapVersion" + AddGXMethod "SUB", "GXMapSave" + AddGXMethod "SUB", "GXMapLoad" + AddGXMethod "FUNCTION", "GXMapIsometric" + AddGXMethod "SUB", "GXMapIsometric" + AddGXMethod "SUB", "GXSpriteDraw" + AddGXMethod "SUB", "GXSpriteDrawScaled" + AddGXMethod "SUB", "GXTilesetCreate" + AddGXMethod "SUB", "GXTilesetReplaceImage" + AddGXMethod "SUB", "GXTilesetLoad" + AddGXMethod "SUB", "GXTilesetSave" + AddGXMethod "SUB", "GXTilesetPos" + AddGXMethod "FUNCTION", "GXTilesetWidth" + AddGXMethod "FUNCTION", "GXTilesetHeight" + AddGXMethod "FUNCTION", "GXTilesetColumns" + AddGXMethod "FUNCTION", "GXTilesetRows" + AddGXMethod "FUNCTION", "GXTilesetFilename" + AddGXMethod "FUNCTION", "GXTilesetImage" + AddGXMethod "SUB", "GXTilesetAnimationCreate" + AddGXMethod "SUB", "GXTilesetAnimationAdd" + AddGXMethod "SUB", "GXTilesetAnimationRemove" + AddGXMethod "FUNCTION", "GXTilesetAnimationFrames" + AddGXMethod "FUNCTION", "GXTilesetAnimationSpeed" + AddGXMethod "SUB", "GXTilesetAnimationSpeed" + AddGXMethod "FUNCTION", "GXFontCreate" + AddGXMethod "SUB", "GXFontCreate" + AddGXMethod "FUNCTION", "GXFontWidth" + AddGXMethod "FUNCTION", "GXFontHeight" + AddGXMethod "FUNCTION", "GXFontCharSpacing" + AddGXMethod "SUB", "GXFontCharSpacing" + AddGXMethod "FUNCTION", "GXFontLineSpacing" + AddGXMethod "SUB", "GXFontLineSpacing" + AddGXMethod "SUB", "GXDrawText" + AddGXMethod "FUNCTION", "GXDebug" + AddGXMethod "SUB", "GXDebug" + AddGXMethod "FUNCTION", "GXDebugScreenEntities" + AddGXMethod "SUB", "GXDebugScreenEntities" + AddGXMethod "FUNCTION", "GXDebugFont" + AddGXMethod "SUB", "GXDebugFont" + AddGXMethod "FUNCTION", "GXDebugTileBorderColor" + AddGXMethod "SUB", "GXDebugTileBorderColor" + AddGXMethod "FUNCTION", "GXDebugEntityBorderColor" + AddGXMethod "SUB", "GXDebugEntityBorderColor" + AddGXMethod "FUNCTION", "GXDebugEntityCollisionColor" + AddGXMethod "SUB", "GXDebugEntityCollisionColor" + AddGXMethod "SUB", "GXKeyInput" + AddGXMethod "FUNCTION", "GXKeyDown" + AddGXMethod "SUB", "GXDeviceInputDetect" + AddGXMethod "FUNCTION", "GXDeviceInputTest" + AddGXMethod "FUNCTION", "GXDeviceName" + AddGXMethod "FUNCTION", "GXDeviceTypeName" + AddGXMethod "FUNCTION", "GXInputTypeName" + AddGXMethod "FUNCTION", "GXKeyButtonName" + + ' Supporting Libraries + AddGXConst "GX_CR" + AddGXConst "GX_LF" + AddGXConst "GX_CRLF" + + AddGXMethod "FUNCTION", "GXSTR_LPad" + AddGXMethod "FUNCTION", "GXSTR_RPad" + AddGXMethod "FUNCTION", "GXSTR_Replace" + ' AddGXMethod "FUNCTION", "GXSTR_Split" + +End Sub + +Sub InitQBMethods + ' QB64 Methods + ' ---------------------------------------------------------- + AddQBMethod "FUNCTION", "_Alpha32" + AddQBMethod "FUNCTION", "_Atan2" + AddQBMethod "FUNCTION", "_Blue" + AddQBMethod "FUNCTION", "_Blue32" + AddQBMethod "SUB", "_Delay" + AddQBMethod "FUNCTION", "_FontWidth" + AddQBMethod "FUNCTION", "_Green" + AddQBMethod "FUNCTION", "_Green32" + AddQBMethod "FUNCTION", "_Height" + AddQBMethod "FUNCTION", "_InStrRev" + AddQBMethod "SUB", "_Limit" + AddQBMethod "FUNCTION", "_KeyDown" + AddQBMethod "FUNCTION", "_KeyHit" + AddQBMethod "FUNCTION", "_MouseButton" + AddQBMethod "FUNCTION", "_MouseInput" + AddQBMethod "FUNCTION", "_MouseX" + AddQBMethod "FUNCTION", "_MouseY" + AddQBMethod "FUNCTION", "_NewImage" + AddQBMethod "FUNCTION", "_Pi" + AddQBMethod "SUB", "_PrintString" + AddQBMethod "FUNCTION", "_PrintWidth" + AddQBMethod "FUNCTION", "_Red" + AddQBMethod "FUNCTION", "_Red32" + AddQBMethod "FUNCTION", "_RGB" + AddQBMethod "FUNCTION", "_RGB32" + AddQBMethod "FUNCTION", "_Round" + AddQBMethod "FUNCTION", "_ScreenExists" + AddQBMethod "SUB", "_Title" + AddQBMethod "FUNCTION", "_Trim" + AddQBMethod "FUNCTION", "_Width" + + ' QB 4.5 Methods + ' --------------------------------------------------------------------------- + AddQBMethod "FUNCTION", "Abs" + AddQBMethod "FUNCTION", "Asc" + AddQBMethod "FUNCTION", "Atn" + AddQBMethod "FUNCTION", "Chr$" + AddQBMethod "SUB", "Circle" + AddQBMethod "SUB", "Cls" + AddQBMethod "SUB", "Color" + AddQBMethod "FUNCTION", "Command$" + AddQBMethod "FUNCTION", "Cos" + AddQBMethod "FUNCTION", "Exp" + AddQBMethod "FUNCTION", "Fix" + AddQBMethod "SUB", "Input" + AddQBMethod "FUNCTION", "InKey$" + AddQBMethod "FUNCTION", "InStr" + AddQBMethod "FUNCTION", "Int" + AddQBMethod "FUNCTION", "Left$" + AddQBMethod "FUNCTION", "LCase$" + AddQBMethod "FUNCTION", "Len" + AddQBMethod "SUB", "Line" + AddQBMethod "SUB", "Locate" + AddQBMethod "FUNCTION", "Log" + AddQBMethod "FUNCTION", "LTrim$" + AddQBMethod "FUNCTION", "Mid$" + AddQBMethod "SUB", "Print" + AddQBMethod "SUB", "PSet" + AddQBMethod "FUNCTION", "Right$" + AddQBMethod "FUNCTION", "RTrim$" + AddQBMethod "FUNCTION", "Rnd" + AddQBMethod "SUB", "Screen" + AddQBMethod "FUNCTION", "Sgn" + AddQBMethod "FUNCTION", "Sin" + AddQBMethod "SUB", "Sleep" + AddQBMethod "FUNCTION", "Sqr" + AddQBMethod "FUNCTION", "Str$" + AddQBMethod "SUB", "Swap" + AddQBMethod "FUNCTION", "Tan" + AddQBMethod "FUNCTION", "Timer" + AddQBMethod "FUNCTION", "UBound" + AddQBMethod "FUNCTION", "UCase$" + AddQBMethod "FUNCTION", "Val" + + ' QBJS-only language features + ' -------------------------------------------------------------------------------- + AddSystemType "FETCHRESPONSE", "ok:INTEGER,status:INTEGER,statusText:STRING,text:STRING" + AddQBMethod "FUNCTION", "Fetch" + AddQBMethod "FUNCTION", "FromJSON" + AddQBMethod "FUNCTION", "ToJSON" +End Sub + +'$include: '../../gx/gx/gx_str.bm' diff --git a/tools/webserver.bas b/tools/webserver.bas new file mode 100644 index 0000000..29ecd17 --- /dev/null +++ b/tools/webserver.bas @@ -0,0 +1,360 @@ +' HTTP 1.1 Compliant Web Server +' Author: luke +' Source: https://www.qb64.org/forum/index.php?topic=2052.0 +' This program is made available for you to use, modify and distribute it as you wish, +' all under the condition you do not claim original authorship. +'$ExeIcon:'./../gx/resource/gx.ico' +$Console:Only +Option _Explicit +DefLng A-Z + +Const MAX_CONNECTIONS = 8 +Dim PORT As Integer: PORT = 8080 +If _CommandCount > 0 Then + PORT = Val(Command$(1)) +End If + +Const FALSE = 0 +Const TRUE = -1 +Dim Shared CRLF As String +CRLF = Chr$(13) + Chr$(10) +Const HTTP_10 = 1 +Const HTTP_11 = 11 +Const HTTP_GET = 1 +Const HTTP_HEAD = 2 +Const HTTP_POST = 3 +Type connection_t + handle As Long + read_buf As String + http_version As Integer + method As Integer + request_uri As String +End Type + +Type http_error_t + code As Integer + message As String + connection As Integer +End Type + +Type file_error_t + failed As Integer + code As Integer +End Type + +Dim i +Dim num_active_connections +Dim server_handle +Dim Shared Connections(1 To MAX_CONNECTIONS) As connection_t +Dim Shared Http_error_info As http_error_t +Dim Shared File_error_info As file_error_t + +On Error GoTo error_handler + +server_handle = _OpenHost("TCP/IP:" + LTrim$(Str$(PORT))) +Print "Listening on port:" + Str$(PORT) +Do + If num_active_connections < MAX_CONNECTIONS Then + Dim new_connection + new_connection = _OpenConnection(server_handle) + If new_connection Then + num_active_connections = num_active_connections + 1 + For i = 1 To MAX_CONNECTIONS + If Connections(i).handle = 0 Then + Dim empty_connection As connection_t + Connections(i) = empty_connection + Connections(i).handle = new_connection + num_active_connections = num_active_connections - 1 + Exit For + End If + Next i + End If + End If + + For i = 1 To MAX_CONNECTIONS + If Connections(i).handle Then + Dim buf$ + Get #Connections(i).handle, , buf$ + If buf$ <> "" Then + Connections(i).read_buf = Connections(i).read_buf + buf$ + process_request i + http_error_complete: + End If + End If + Next i + _Limit 240 +Loop + + + +error_handler: +If Err = 100 Then 'HTTP error + Print "HTTP error"; Http_error_info.code; Http_error_info.message; " for connection"; Http_error_info.connection + Resume http_error_complete +End If +Print "error"; Err; "on line"; _ErrorLine +End + +file_error_handler: +File_error_info.failed = TRUE +File_error_info.code = Err +Resume Next + +Sub http_send_status (c, code, message As String) + Dim s$ + s$ = "HTTP/1.1" + Str$(code) + " " + message + CRLF + Put #Connections(c).handle, , s$ +End Sub + +Sub http_send_header (c, header As String, value As String) + Dim s$ + s$ = header + ": " + value + CRLF + Put #Connections(c).handle, , s$ +End Sub + +Sub http_end_headers (c) + Put #Connections(c).handle, , CRLF +End Sub + +Sub http_send_body (c, body As String) + Put #Connections(c).handle, , body +End Sub + +Sub http_do_get (c) + Dim filepath As String, filedata As String + Dim fh + filepath = get_requested_filesystem_path(c) + Print filepath + If Not _FileExists(filepath) Then http_error 404, "Not Found", c + + On Error GoTo file_error_handler + fh = FreeFile + File_error_info.failed = FALSE + Open filepath For Binary As #fh + On Error GoTo error_handler + If File_error_info.failed Then http_error 403, "Permission Denied", c + + 'Doing this all in one go isn't healthy for a number of reasons (memory usage, starving other clients) + 'It should be done in chunks in the main loop + filedata = Space$(LOF(fh)) + Get #fh, , filedata + Close #fh + http_send_status c, 200, "OK" + http_send_header c, "Content-Length", LTrim$(Str$(Len(filedata))) + http_send_header c, "Access-Control-Allow-Origin", "true" + http_send_header c, "Connection", "close" + http_end_headers c + http_send_body c, filedata + close_connection c +End Sub + +Sub http_do_head (c) + Print "http_do_head" + Dim s$ + s$ = "HTTP/1.1 200 OK" + CRLF + CRLF + Put #Connections(c).handle, , s$ +End Sub + +Sub http_do_post (c) + Print "POST" + Print Connections(c).request_uri + Dim path As String + path = Right$(Connections(c).request_uri, Len(Connections(c).request_uri) - 1) + Dim idx As Integer + idx = _InStrRev(path, "/") + path = Left$(path, idx) + + Dim basFile As String + basFile = path + "game.bas" + Dim jsFile As String + jsFile = path + "game.js" + + If _FileExists(basFile) Then Kill basFile + Dim fh + fh = FreeFile + Open basFile For Binary As #fh + Put #fh, , Connections(c).read_buf + Close #fh + + Shell "qb2js " + basFile + " > " + jsFile + + close_connection c +End Sub + +Sub close_connection (c) + Close #Connections(c).handle + Connections(c).handle = 0 +End Sub + +Function get_requested_filesystem_path$ (c) + '7230 5.3 also 3986 for URI + 'Origin form only for now + Dim raw_path As String + raw_path = Connections(c).request_uri + If Left$(raw_path, 1) <> "/" Then http_error 400, "Malformed URI", c + + Dim hash, questionmark, path_len + hash = InStr(raw_path, "#") 'Clients shouldn't be sending fragments, but we will gracefully ignore them + questionmark = InStr(raw_path, "?") + path_len = Len(raw_path) + If hash > 0 Then path_len = hash - 1 + 'If questionmark > 0 And questionmark < hash Then path_len = questionmark - 1 + If questionmark > 0 Then path_len = questionmark - 1 + ' Query strings are ignored for now + + 'Dim cwd As String + 'cwd = _CWD$ + '$If WIN Then + ' 'raw_path = GXSTR_Replace(raw_path, "/", "\") + ' cwd = GXSTR_Replace(cwd, "\", "/") + '$End If + + 'Print "--> " + Left$(raw_path, path_len) + + get_requested_filesystem_path = _STARTDIR$ + cannonicalise_path(percent_decode(Left$(raw_path, path_len))) +End Function + +Function percent_decode$ (raw_string As String) + Dim final_string As String, hexchars As String + Dim i, c + For i = 1 To Len(raw_string) + c = Asc(raw_string, i) + If c = 37 Then '% + hexchars = Mid$(raw_string, i + 1, 2) + If Len(hexchars) = 2 And InStr("0123456789abcdefABCDEF", Left$(hexchars, 1)) > 0 And InStr("0123456789abcdefABCDEF", Right$(hexchars, 1)) > 0 Then + final_string = final_string + Chr$(Val("&H" + hexchars)) + Else + 'String ends in something like "%1", or is invalid hex characters + final_string = final_string + "%" + hexchars + End If + i = i + Len(hexchars) + Else + final_string = final_string + Chr$(c) + End If + Next i + percent_decode = final_string +End Function + + +Function cannonicalise_path$ (raw_path As String) + Dim path As String + ReDim segments(1 To 1) As String + Dim i, uplevels + split raw_path, "/", segments() + For i = UBound(segments) To 1 Step -1 + If segments(i) = "." Or segments(i) = "" Then + _Continue + ElseIf segments(i) = ".." Then + uplevels = uplevels + 1 + Else + If uplevels = 0 Then + path = "/" + segments(i) + path + Else + uplevels = uplevels - 1 + End If + End If + Next i + If path = "" Then path = "/" + 'Note: if uplevels > 0 at this point, the path attempted to go above the root + 'This is usually a client trying to be naughty + cannonicalise_path = path +End Function + +'https://www.qb64.org/forum/index.php?topic=1607.0 +Sub split (SplitMeString As String, delim As String, loadMeArray() As String) + Dim curpos As Long, arrpos As Long, LD As Long, dpos As Long 'fix use the Lbound the array already has + curpos = 1: arrpos = LBound(loadMeArray): LD = Len(delim) + dpos = InStr(curpos, SplitMeString, delim) + Do Until dpos = 0 + loadMeArray(arrpos) = Mid$(SplitMeString, curpos, dpos - curpos) + arrpos = arrpos + 1 + If arrpos > UBound(loadMeArray) Then ReDim _Preserve loadMeArray(LBound(loadMeArray) To UBound(loadMeArray) + 1000) As String + curpos = dpos + LD + dpos = InStr(curpos, SplitMeString, delim) + Loop + loadMeArray(arrpos) = Mid$(SplitMeString, curpos) + ReDim _Preserve loadMeArray(LBound(loadMeArray) To arrpos) As String 'get the ubound correct +End Sub + + +Sub process_request (c) + Dim eol + Dim l As String + Do + eol = InStr(Connections(c).read_buf, CRLF) + If eol = 0 Then Exit Sub + l = Left$(Connections(c).read_buf, eol - 1) + Connections(c).read_buf = Mid$(Connections(c).read_buf, eol + 2) + If Connections(c).http_version = 0 Then 'First line not yet read + process_start_line c, l + Else + If l = "" Then + 'headers complete; act upon request now + Select Case Connections(c).method + Case HTTP_GET + http_do_get c + Case HTTP_POST + http_do_post c + Case HTTP_HEAD + http_do_head c + End Select + Exit Sub + Else + process_header c, l + End If + End If + Loop +End Sub + +Sub process_start_line (c, l As String) + '7230 3.1.1 + 'METHOD uri HTTP/x.y + Dim sp1, sp2 + sp1 = InStr(l, " ") + If sp1 = 0 Then http_error 400, "Bad Request", c + + '7231 4.3 + Select Case Left$(l, sp1 - 1) + Case "GET" + Connections(c).method = HTTP_GET + Case "HEAD" + Connections(c).method = HTTP_HEAD + Case "POST" + Connections(c).method = HTTP_POST + Case Else + http_error 501, "Not Implemented", c + End Select + + sp2 = InStr(sp1 + 1, l, " ") + If sp2 = 0 Or sp2 - sp1 = 1 Then http_error 400, "Bad Request", c + Connections(c).request_uri = Mid$(l, sp1 + 1, sp2 - sp1 - 1) + + '7230 2.6 + If Mid$(l, sp2 + 1, 5) <> "HTTP/" Then + http_error 400, "Bad Request", c + End If + Select Case Mid$(l, sp2 + 6) + Case "1.0" + Connections(c).http_version = HTTP_10 + Case "1.1" + Connections(c).http_version = HTTP_11 + Case Else + http_error 505, "HTTP Version Not Supported", c + End Select +End Sub + +Sub process_header (c, l As String) + 'All headers ignored for now +End Sub + +Sub http_error (code, message As String, connection) + http_send_status connection, code, message + http_send_header connection, "Content-Length", "0" + http_send_header connection, "Connection", "close" + http_end_headers connection + close_connection connection + Http_error_info.code = code + Http_error_info.message = message + Http_error_info.connection = connection + Error 100 +End Sub diff --git a/util/shorty.min.js b/util/shorty.min.js new file mode 100644 index 0000000..4525292 --- /dev/null +++ b/util/shorty.min.js @@ -0,0 +1,2 @@ +/* shorty.js - by enki - https://enkimute.github.io */ +!function(t,i,e){"undefined"!=typeof module&&module.exports?module.exports=e():"function"==typeof define&&define.amd?define(t,e):i[t]=e()}("Shorty",this,function(){function t(t){this.tokensize=t||10,this.reset(!0)}return t.prototype.reset=function(t){t===!0&&(this.nodes=[{up:0,weight:0}],this.nyt=0,this.nodecount=0),this.data="",this.curpos=0,this.bitCount=7,this.bitChar=0},t.prototype.findNode=function(t){for(var i=this.nodes.length-1;i>0;i--)if("undefined"!=typeof this.nodes[i].symbol&&this.nodes[i].symbol==t)return i;return 0},t.prototype.addNode=function(t){return this.nodecount>=2046?0:(this.nodes[++this.nodecount]={up:this.nyt,symbol:t,weight:1},this.nodes[++this.nodecount]={up:this.nyt,weight:0},this.nodes[this.nyt].weight+=1,this.nyt=this.nodecount,this.nodes[this.nodecount-2].up!=this.nodecount-2&&this.balanceNode(this.nodes[this.nodecount-2].up),this.nodecount-2)},t.prototype.swapNode=function(t,i){var e=this.nodes[t].symbol,s=this.nodes[i].symbol,o=this.nodes[t].weight;this.nodes[t].symbol=s,this.nodes[i].symbol=e,this.nodes[t].weight=this.nodes[i].weight,this.nodes[i].weight=o;for(var h=this.nodes.length-1;h>0;h--)this.nodes[h].up==t?this.nodes[h].up=i:this.nodes[h].up==i&&(this.nodes[h].up=t)},t.prototype.balanceNode=function(t){for(;;){for(var i=t,e=this.nodes[t].weight;i>1&&this.nodes[i-1].weight==e;)i--;if(i!=t&&i!=this.nodes[t].up&&(this.swapNode(i,t),t=i),this.nodes[t].weight++,this.nodes[t].up==t)return;t=this.nodes[t].up}},t.prototype.emitNode=function(t){for(var i=[];0!=t;)i.unshift(t%2),t=this.nodes[t].up;for(var e=0;e8&&this.emitBit(8&i),this.tokensize>4&&this.emitBit(4&i),this.tokensize>2&&this.emitBit(2&i),this.tokensize>1&&this.emitBit(1&i);for(var e=0;e8?8*this.readBit():0)+(this.tokensize>4?4*this.readBit():0)+(this.tokensize>2?2*this.readBit():0)+(this.tokensize>1?this.readBit():0)+1,i="";t--;)i+=this.readByte();return i}for(var e=0;;){var s=this.readBit();if(void 0==this.nodes[e].symbol)for(var o=0;;o++)if(this.nodes[o].up==e&&o!=e&&o%2==s){e=o;break}if(void 0!=this.nodes[e].symbol||0==this.nodes[e].weight){if(this.nodes[e].weight)return this.nodes[e].symbol;for(var t=(this.tokensize>8?8*this.readBit():0)+(this.tokensize>4?4*this.readBit():0)+(this.tokensize>2?2*this.readBit():0)+(this.tokensize>1?this.readBit():0)+1,i="";t--;)i+=this.readByte();return i}}},t.prototype.emitBit=function(t){t&&(this.bitChar+=1<=0;i--)this.emitBit(t>>i&1)},t.prototype.readBit=function(){if(this.curpos==8*this.data.length)throw"done";var t=this.data.charCodeAt(this.curpos>>3)>>(7-this.curpos&7)&1;return this.curpos++,t},t.prototype.readByte=function(){res=0;for(var t=0;8>t;t++)res+=(128>>t)*this.readBit();return String.fromCharCode(res)},t.prototype.deflate=function(t){var i,e,s,o=t.length;for(this.reset(),e=0;o>e;e++){if(i=t[e],this.tokensize>1)if(/[a-zA-Z]/.test(i))for(;o>e+1&&i.lengthe+1&&i.length=0;e++){var s=this.readNode();i+=s;var o=this.findNode(s);o?this.balanceNode(o):this.addNode(s)}}catch(h){}return i},t}); \ No newline at end of file