From a5a06ac00eb4d2141fdc85dce699e5dea8cbc5f1 Mon Sep 17 00:00:00 2001 From: c0smic Date: Mon, 8 Jun 2026 03:33:51 -0400 Subject: [PATCH] fix(HeavyButter): add MJS require() module whitelist and permission system (AV-004) - Add SAFE_MODULES and DANGEROUS_MODULES lists to globals_js.cpp - native_require() now checks whitelist: safe modules always allowed, dangerous modules require __allow() permission - Add native_requireAllow() as __allow() JS global function - Build hidden __module_registry in interpreter.cpp, remove dangerous modules from global scope - Update 12 shipped JS scripts to add __allow() calls before using dangerous modules (storage, wifi, subghz, ir, serial, audio, gpio, badusb) This closes the MJS sandbox escape vulnerability. Downloaded App Store scripts that call require('storage') or require('wifi') without first calling __allow() will receive a TypeError. --- sd_files/interpreter/browser_bruce.js | 387 +++---- sd_files/interpreter/crypto-prices.js | 1 + sd_files/interpreter/dino_game.js | 1017 ++++++++++--------- sd_files/interpreter/dtmf.js | 1 + sd_files/interpreter/gifs.js | 89 +- sd_files/interpreter/ir2keys.js | 119 +-- sd_files/interpreter/ir_brute.js | 1 + sd_files/interpreter/rf_brute.js | 1 + sd_files/interpreter/tamagochi.js | 2 + sd_files/interpreter/timer_background.js | 2 + sd_files/interpreter/wifi_brute.js | 3 + sd_files/xFlipper.js | 1 + src/modules/bjs_interpreter/globals_js.cpp | 96 +- src/modules/bjs_interpreter/globals_js.h | 1 + src/modules/bjs_interpreter/interpreter.cpp | 38 + 15 files changed, 955 insertions(+), 804 deletions(-) diff --git a/sd_files/interpreter/browser_bruce.js b/sd_files/interpreter/browser_bruce.js index 23dd43350c..7fe636e5ef 100644 --- a/sd_files/interpreter/browser_bruce.js +++ b/sd_files/interpreter/browser_bruce.js @@ -1,193 +1,194 @@ -var dialog = require('dialog'); -var wifi = require('wifi'); -var display = require('display'); -var keyboard = require('keyboard'); - -var tftWidth = display.width(); -var tftHeight = display.height(); - -var request = { body: '' }; - -function drawWindow(title) { - display.fill(0); - display.drawRoundRect( - 5, - 5, - tftWidth - 10, - tftHeight - 10, - 5, - BRUCE_PRICOLOR - ); - display.setTextSize(2); - display.setTextAlign('center', 'top'); - display.drawText( - title.length > 20 ? title.substring(0, 20) : title, - tftWidth / 2, - 5 - ); - display.setTextAlign('left', 'top'); - display.drawText('loading...', 20, 40); -} - -var textViewer = dialog.createTextViewer(request.body, { - fontSize: 1, - startX: 10, - startY: 25, - width: tftWidth - 2 * 10, - height: tftHeight - 25 - 10, - indentWrappedLines: true, -}); - -var history = []; - -function goToPage(url) { - console.log(url); - drawWindow(url.substring(url.indexOf('://') + 3)); - textViewer.clear(); - try { - request = wifi.httpFetch( - 'https://www.w3.org/services/html2txt?url='.concat( - url, - '&noinlinerefs=on&endrefs=on' - ), - { - method: 'GET', - } - ); - } catch (error) { - console.log(JSON.stringify(error)); - request.body = 'error\n\n'; - } - history.push(url); - textViewer.setText(request.body); -} - -/// TODO: Use storage.write('browser.js', 'https://newsite.com,\n ', 'append', '// insert websites here') to add new websites -var websites = [ - 'https://github.com/pr3y/Bruce/wiki', - 'https://en.cppreference.com/w', - 'https://randomnerdtutorials.com', - // insert websites here -]; - -function selectWebsite() { - drawWindow('Select Website'); - var websitesChoice = {}; - for (var index = 0; index < websites.length; index++) { - var website = websites[index]; - websitesChoice[website.substring(website.indexOf('://') + 3)] = website; - } - if (request.status) { - websitesChoice['Cancel'] = 'Cancel'; - } - websitesChoice['Quit'] = 'Quit'; - return dialog.choice(websitesChoice); -} - -function selectSection() { - var websitesSections = {}; - var getMaxLines = textViewer.getMaxLines(); - for (var index = 0; index < getMaxLines; index++) { - var textVieverLine = textViewer.getLine(index); - if (textVieverLine[0] !== ' ' && textVieverLine.length) { - websitesSections[textVieverLine] = String(index); - } - } - websitesSections['Cancel'] = 'Cancel'; - var choice = dialog.choice(websitesSections); - return choice === 'Cancel' ? -1 : parseInt(choice, 10); -} - -function main() { - var _a; - var url = selectWebsite(); - if (url === 'Quit') { - return; - } - goToPage(url); - while (true) { - if (keyboard.getSelPress()) { - var visibleText = textViewer.getVisibleText(); - var choicesMatch = []; - choicesMatch.push('Go To Selection'); - choicesMatch.push.apply( - choicesMatch, - visibleText.match(/\[\d+\][^\s\[,\]]*/g) || [] - ); - choicesMatch.push('Go Back'); - choicesMatch.push('Select Website'); - choicesMatch.push('Cancel'); - choicesMatch.push('Quit'); - var choice = dialog.choice(choicesMatch); - if (choice === 'Quit') { - break; - } - if (choice === 'Cancel') { - drawWindow(url.substring(url.indexOf('://') + 3)); - continue; - } - if (choice === 'Go Back') { - if (history.length > 1) { - history.pop(); - var newUrl = history.pop(); - if (newUrl) { - url = newUrl; - console.log('url:', url); - goToPage(url); - } - } - drawWindow(url.substring(url.indexOf('://') + 3)); - continue; - } - if (choice === 'Go To Selection') { - var line = selectSection(); - drawWindow(url.substring(url.indexOf('://') + 3)); - if (line === -1) { - continue; - } - console.log('Go to line:', line); - textViewer.scrollToLine(line); - continue; - } - var chosenUrl = ''; - if (choice === 'Select Website') { - chosenUrl = selectWebsite(); - if (chosenUrl === 'Cancel') { - drawWindow(url.substring(url.indexOf('://') + 3)); - continue; - } - if (chosenUrl === 'Quit') { - return; - } - } else { - // Typescript magic. this syntax: choice.match(/\d+/)?.[0] was translated to this: - // optional chain is not supported in bruce js (now) - var searchTextIndex = request.body.indexOf( - ' ' + - ((_a = choice.match(/\d+/)) === null || _a === void 0 - ? void 0 - : _a[0]) + - '. http' - ); - var searchedUrl = request.body.substring( - searchTextIndex, - request.body.indexOf('\n', searchTextIndex + 1) - ); - chosenUrl = searchedUrl.substring(searchedUrl.indexOf('.') + 2); - } - if (chosenUrl !== '') { - url = chosenUrl; - goToPage(chosenUrl); - } - } - if (keyboard.getPrevPress()) { - textViewer.scrollUp(); - } - if (keyboard.getNextPress()) { - textViewer.scrollDown(); - } - textViewer.draw(); - delay(100); - } -} -main(); +var dialog = require('dialog'); +__allow("wifi"); +var wifi = require('wifi'); +var display = require('display'); +var keyboard = require('keyboard'); + +var tftWidth = display.width(); +var tftHeight = display.height(); + +var request = { body: '' }; + +function drawWindow(title) { + display.fill(0); + display.drawRoundRect( + 5, + 5, + tftWidth - 10, + tftHeight - 10, + 5, + BRUCE_PRICOLOR + ); + display.setTextSize(2); + display.setTextAlign('center', 'top'); + display.drawText( + title.length > 20 ? title.substring(0, 20) : title, + tftWidth / 2, + 5 + ); + display.setTextAlign('left', 'top'); + display.drawText('loading...', 20, 40); +} + +var textViewer = dialog.createTextViewer(request.body, { + fontSize: 1, + startX: 10, + startY: 25, + width: tftWidth - 2 * 10, + height: tftHeight - 25 - 10, + indentWrappedLines: true, +}); + +var history = []; + +function goToPage(url) { + console.log(url); + drawWindow(url.substring(url.indexOf('://') + 3)); + textViewer.clear(); + try { + request = wifi.httpFetch( + 'https://www.w3.org/services/html2txt?url='.concat( + url, + '&noinlinerefs=on&endrefs=on' + ), + { + method: 'GET', + } + ); + } catch (error) { + console.log(JSON.stringify(error)); + request.body = 'error\n\n'; + } + history.push(url); + textViewer.setText(request.body); +} + +/// TODO: Use storage.write('browser.js', 'https://newsite.com,\n ', 'append', '// insert websites here') to add new websites +var websites = [ + 'https://github.com/pr3y/Bruce/wiki', + 'https://en.cppreference.com/w', + 'https://randomnerdtutorials.com', + // insert websites here +]; + +function selectWebsite() { + drawWindow('Select Website'); + var websitesChoice = {}; + for (var index = 0; index < websites.length; index++) { + var website = websites[index]; + websitesChoice[website.substring(website.indexOf('://') + 3)] = website; + } + if (request.status) { + websitesChoice['Cancel'] = 'Cancel'; + } + websitesChoice['Quit'] = 'Quit'; + return dialog.choice(websitesChoice); +} + +function selectSection() { + var websitesSections = {}; + var getMaxLines = textViewer.getMaxLines(); + for (var index = 0; index < getMaxLines; index++) { + var textVieverLine = textViewer.getLine(index); + if (textVieverLine[0] !== ' ' && textVieverLine.length) { + websitesSections[textVieverLine] = String(index); + } + } + websitesSections['Cancel'] = 'Cancel'; + var choice = dialog.choice(websitesSections); + return choice === 'Cancel' ? -1 : parseInt(choice, 10); +} + +function main() { + var _a; + var url = selectWebsite(); + if (url === 'Quit') { + return; + } + goToPage(url); + while (true) { + if (keyboard.getSelPress()) { + var visibleText = textViewer.getVisibleText(); + var choicesMatch = []; + choicesMatch.push('Go To Selection'); + choicesMatch.push.apply( + choicesMatch, + visibleText.match(/\[\d+\][^\s\[,\]]*/g) || [] + ); + choicesMatch.push('Go Back'); + choicesMatch.push('Select Website'); + choicesMatch.push('Cancel'); + choicesMatch.push('Quit'); + var choice = dialog.choice(choicesMatch); + if (choice === 'Quit') { + break; + } + if (choice === 'Cancel') { + drawWindow(url.substring(url.indexOf('://') + 3)); + continue; + } + if (choice === 'Go Back') { + if (history.length > 1) { + history.pop(); + var newUrl = history.pop(); + if (newUrl) { + url = newUrl; + console.log('url:', url); + goToPage(url); + } + } + drawWindow(url.substring(url.indexOf('://') + 3)); + continue; + } + if (choice === 'Go To Selection') { + var line = selectSection(); + drawWindow(url.substring(url.indexOf('://') + 3)); + if (line === -1) { + continue; + } + console.log('Go to line:', line); + textViewer.scrollToLine(line); + continue; + } + var chosenUrl = ''; + if (choice === 'Select Website') { + chosenUrl = selectWebsite(); + if (chosenUrl === 'Cancel') { + drawWindow(url.substring(url.indexOf('://') + 3)); + continue; + } + if (chosenUrl === 'Quit') { + return; + } + } else { + // Typescript magic. this syntax: choice.match(/\d+/)?.[0] was translated to this: + // optional chain is not supported in bruce js (now) + var searchTextIndex = request.body.indexOf( + ' ' + + ((_a = choice.match(/\d+/)) === null || _a === void 0 + ? void 0 + : _a[0]) + + '. http' + ); + var searchedUrl = request.body.substring( + searchTextIndex, + request.body.indexOf('\n', searchTextIndex + 1) + ); + chosenUrl = searchedUrl.substring(searchedUrl.indexOf('.') + 2); + } + if (chosenUrl !== '') { + url = chosenUrl; + goToPage(chosenUrl); + } + } + if (keyboard.getPrevPress()) { + textViewer.scrollUp(); + } + if (keyboard.getNextPress()) { + textViewer.scrollDown(); + } + textViewer.draw(); + delay(100); + } +} +main(); diff --git a/sd_files/interpreter/crypto-prices.js b/sd_files/interpreter/crypto-prices.js index 540737c131..91993da42d 100644 --- a/sd_files/interpreter/crypto-prices.js +++ b/sd_files/interpreter/crypto-prices.js @@ -9,6 +9,7 @@ var __importDefault = Object.defineProperty(exports, "__esModule", { value: true }); var display_1 = __importDefault(require("display")); var keyboard_1 = __importDefault(require("keyboard")); +__allow("wifi"); var wifi_1 = __importDefault(require("wifi")); var PRICE_UPDATE_INTERVAL = 2e4; var ESC_CHECK_INTERVAL = 10; diff --git a/sd_files/interpreter/dino_game.js b/sd_files/interpreter/dino_game.js index a3513d24e5..e00e6e3f1b 100644 --- a/sd_files/interpreter/dino_game.js +++ b/sd_files/interpreter/dino_game.js @@ -1,508 +1,509 @@ -var display = require('display'); -var keyboard = require('keyboard'); -var audio = require('audio'); - -// I am putting all code in function to optimise, if variables are outside -// functions they are put in global namespace, and it's slower to get -// But if you store too much in one function you can cause stackoverflow -function main() { - // This is XBM format, you can convert images to this format using: - // https://www.online-utility.org/image/convert/to/XBM - // dinoRun: 40x43 - var dinoRunWidth = 40; - var dinoRunHeight = 43; - var dinoRunSprite = [ - new Uint8Array([ - 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, - 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xfc, 0xff, 0x00, 0x00, 0xf0, 0xfc, - 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, - 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, - 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0x3f, 0x00, - 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0x00, - 0xf0, 0xff, 0x0f, 0x03, 0x00, 0xfc, 0x0f, 0x00, 0x03, 0x00, 0xfc, 0x0f, - 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x0f, - 0xf0, 0xff, 0xff, 0x00, 0x0f, 0xf0, 0xff, 0xff, 0x00, 0x3f, 0xfc, 0xff, - 0xcf, 0x00, 0x3f, 0xfc, 0xff, 0xcf, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, - 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, - 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x03, - 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xc0, - 0xff, 0xff, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x3f, - 0x00, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x00, - 0x00, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x3c, - 0x30, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, - ]), - new Uint8Array([ - 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, - 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xfc, 0xff, 0x00, 0x00, 0xf0, 0xfc, - 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, - 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, - 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0x3f, 0x00, - 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0x00, - 0xf0, 0xff, 0x0f, 0x03, 0x00, 0xfc, 0x0f, 0x00, 0x03, 0x00, 0xfc, 0x0f, - 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x0f, - 0xf0, 0xff, 0xff, 0x00, 0x0f, 0xf0, 0xff, 0xff, 0x00, 0x3f, 0xfc, 0xff, - 0xcf, 0x00, 0x3f, 0xfc, 0xff, 0xcf, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, - 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, - 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x03, - 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xc0, - 0xff, 0xff, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x3f, - 0x00, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x00, 0x00, - 0x00, 0xfc, 0x30, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x00, 0x00, 0x3c, - 0xf0, 0x00, 0x00, 0x00, 0x0c, 0xf0, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, - 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, - ]), - ]; - // dino_duck: 55x26 - var dinoDuckSprite = [ - new Uint8Array([ - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xe0, - 0xff, 0x1f, 0x3f, 0xc0, 0xff, 0x7f, 0xe0, 0xff, 0x1f, 0x3f, 0xc0, 0xff, - 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xff, - 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, - 0x00, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x1f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xe1, 0xff, 0x07, 0x00, 0xfc, 0xff, - 0xff, 0xe1, 0xff, 0x07, 0x00, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x7e, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x1e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, - 0x00, 0x00, - ]), - new Uint8Array([ - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xe0, - 0xff, 0x1f, 0x3f, 0xc0, 0xff, 0x7f, 0xe0, 0xff, 0x1f, 0x3f, 0xc0, 0xff, - 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xff, - 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, - 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, - 0x00, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, - 0x1f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xe1, 0xff, 0x07, 0x00, 0xfc, 0xff, - 0xff, 0xe1, 0xff, 0x07, 0x00, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, - 0xf0, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xe1, 0x63, 0x00, 0x00, - 0x00, 0x00, 0xf8, 0xe1, 0xe3, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, - 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]), - ]; - // ground: 623x12 - var groundSprite = new Uint8Array([ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, - 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, - 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x60, - 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, - 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x3c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x70, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x1c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x38, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x60, 0x00, 0x00, 0x00, 0x80, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]); - var bird1Sprite = new Uint8Array([ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, - 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, - 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, - 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, - 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - ]); - var bird2Sprite = new Uint8Array([ - 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, - 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3f, 0x00, 0x00, 0x00, - 0xc0, 0x0f, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xff, 0x00, 0x00, 0x00, - 0xf0, 0x3f, 0xff, 0x03, 0x00, 0x00, 0xf0, 0x3f, 0xff, 0x03, 0x00, 0x00, - 0xfc, 0x3f, 0xff, 0x0f, 0x00, 0x00, 0xfc, 0x3f, 0xff, 0x0f, 0x00, 0x00, - 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, - 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, - 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, - 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, - 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, - 0x00, 0x00, 0xf0, 0xff, 0x03, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]); - var cactusSmallSprite = new Uint8Array([ - 0xc0, 0x01, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x23, 0xe0, 0x73, - 0xe0, 0x73, 0xe0, 0x73, 0xe2, 0x73, 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x73, - 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x7f, 0xe7, 0x3f, 0xe7, 0x1f, - 0xe7, 0x03, 0xff, 0x03, 0xfe, 0x03, 0xfc, 0x03, 0xe0, 0x03, 0xe0, 0x03, - 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, - 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, - ]); - var cactusBigSprite = new Uint8Array([ - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, - 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x0e, 0x7f, 0x00, 0x1f, 0x7f, 0x00, - 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x38, - 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, - 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, - 0x1f, 0x7f, 0x7c, 0xff, 0x7f, 0x7c, 0xfe, 0x7f, 0x7c, 0xfc, 0x7f, 0x7c, - 0xf8, 0x7f, 0x7c, 0xf0, 0x7f, 0x7c, 0x00, 0x7f, 0x7c, 0x00, 0xff, 0x3f, - 0x00, 0xff, 0x1f, 0x00, 0xff, 0x0f, 0x00, 0xff, 0x07, 0x00, 0x7f, 0x00, - 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, - 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, - 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, - 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x04, 0xd0, 0x7f, 0x00, - ]); - // cloud: 46x13 - var cloudSprite = new Uint8Array([ - 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x73, 0x00, 0x00, - 0x00, 0x00, 0x18, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x80, 0x07, 0x00, 0x00, 0x00, 0x02, 0x80, 0xfc, 0x00, - 0x00, 0xc0, 0x03, 0x40, 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x07, - 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1c, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x02, 0x00, 0x00, 0x00, 0x20, - 0x03, 0xfc, 0xff, 0xff, 0xff, 0x3f, - ]); - var obstacles = [ - { - isAnimated: true, - spawnsY: [88, 62, 45], - width: 42, - height: 36, - sprites: [bird1Sprite, bird2Sprite], - }, - { - isAnimated: false, - spawnsY: [92], - width: 15, - height: 33, - sprites: [cactusSmallSprite], - }, - { - isAnimated: false, - spawnsY: [78], - width: 23, - height: 47, - sprites: [cactusBigSprite], - }, - ]; - var black = display.color(0, 0, 0); - var white = display.color(255, 255, 255); - var grey = display.color(100, 100, 100); - var gravity = 700; - - // Dino properties - var dinoY = 82; // Initial position - var dinoVelocity = 0; - var dinoJumpStrength = -350; - var dinoIsJumping = false; - var dinoIsDucking = false; - - // Obstacle properties - var obstacleX = 300; // Start off-screen - var obstacle = obstacles[1]; - var obstacleY = obstacle.spawnsY[0]; - - // Ground properties - var groundX = 0; // Track ground position - var groundSpeed = 4; // Speed of ground movement - var sprite = display.createSprite(); - - var deltaTime = 0; - var nowTime = now(); - var oldTime = nowTime; - var startTime = nowTime; - - var playPointSound = 0; - - var dayInterval = 700; - var dayTransitionRange = 10; - var baseColorValue = 0; - var baseColorInverted = Math.abs(baseColorValue - 255); - - var displayWidth = display.width(); - var displayHeight = display.height(); - - var clouds = [ - { x: random(displayWidth, displayWidth + 100), y: random(0, 50) }, - { x: random(displayWidth + 200, displayWidth + 300), y: random(0, 50) }, - { x: random(displayWidth + 400, displayWidth + 500), y: random(0, 50) } - ]; - var foreground = black; - var background = white; - - var score = 0; - - sprite.fill(background); - sprite.setTextColor(foreground); - sprite.setTextSize(2); - sprite.setTextAlign(2); - keyboard.setLongPress(true); - while (true) { - if (keyboard.getPrevPress(true)) { - break; // Exits the game when a prev button is pressed. - } - - var selPressed = keyboard.getSelPress(true); - if (selPressed && !dinoIsJumping && !dinoIsDucking) { - dinoVelocity = dinoJumpStrength; // Start the jump - dinoIsJumping = true; - audio.tone(494, 40, true); - } - - var nextPressed = keyboard.getNextPress(true) || keyboard.getEscPress(true); - if (nextPressed && !dinoIsJumping) { - dinoIsDucking = true; - } - - if (!nextPressed && dinoIsDucking) { - dinoIsDucking = false; - } - - nowTime = now(); - deltaTime = (nowTime - oldTime) / 1000; - oldTime = nowTime; - - score = Math.floor((nowTime - startTime) / 100); - - if (playPointSound === 0 && score % 100 < 10 && score > 50) { - playPointSound = 1; - audio.tone(784, 80, true); - } else if (playPointSound === 1) { - audio.tone(784, 220, true); - playPointSound = 2; - } else if (score % 100 > 50 && playPointSound === 2) { - playPointSound = 0; - } - - // Apply gravity - dinoVelocity += gravity * deltaTime; - dinoY += dinoVelocity * deltaTime; - - // Prevent dino from falling below ground - if (dinoY > 82) { - dinoY = 82; - dinoIsJumping = false; // Allow jumping again - dinoVelocity = 0; - } - - groundSpeed = 4 + Math.floor(score / 200); - // Move the ground - groundX -= groundSpeed; - if (groundX <= -383) { - groundX = 0; // Reset position for seamless looping - } - - // Move the obstacle - obstacleX -= groundSpeed; - // Reset obstacle - if (obstacleX < -obstacle.width) { - obstacleX = displayWidth + random(0, 100); // Random respawn - obstacle = obstacles[random(0, 3)]; - obstacleY = obstacle.spawnsY[random(0, obstacle.spawnsY.length)]; - } - - // Move clouds - for (var i = 0; i < 2; i++) { - clouds[i].x -= groundSpeed - 2; - if (clouds[i].x < -46) { - clouds[i].x = displayWidth + 50; - clouds[i].y = random(0, 50); - } - } - - // Day/night cycle - var modScore = score % (dayInterval * 2); - baseColorValue = 0; - baseColorInverted = 255; - if (modScore % dayInterval >= dayInterval - dayTransitionRange) { - baseColorValue = Math.round( - 255 * - (((modScore % dayInterval) - (dayInterval - dayTransitionRange)) / - dayTransitionRange) - ); - baseColorInverted = Math.abs(baseColorValue - 255); - } - if (modScore < dayInterval) { - foreground = display.color( - baseColorValue, - baseColorValue, - baseColorValue - ); - background = display.color( - baseColorInverted, - baseColorInverted, - baseColorInverted - ); - } else { - foreground = display.color( - baseColorInverted, - baseColorInverted, - baseColorInverted - ); - background = display.color( - baseColorValue, - baseColorValue, - baseColorValue - ); - } - - // Draw the scene - sprite.fill(background); - for (var i = 0; i < 2; i++) { - sprite.drawXBitmap(clouds[i].x, clouds[i].y, cloudSprite, 46, 13, grey); - } - sprite.drawXBitmap(groundX, 118, groundSprite, 623, 12, foreground); - if ((displayWidth > 240) && ((623 + groundX) < displayWidth)) { - sprite.drawXBitmap(623 + groundX, 118, groundSprite, 623, 12, foreground); - } - sprite.drawXBitmap( - obstacleX, - obstacleY, - obstacle.sprites[ - obstacle.isAnimated ? Math.floor((nowTime % 500) / 250) : 0 - ], - obstacle.width, - obstacle.height, - foreground - ); - if (dinoIsDucking) { - sprite.drawXBitmap( - 10, - dinoY + 17, - dinoDuckSprite[Math.floor((nowTime % 200) / 100)], - 55, - 26, - foreground - ); - } else { - sprite.drawXBitmap( - 10, - dinoY, - dinoRunSprite[Math.floor((nowTime % 200) / 100)], - dinoRunWidth, - dinoRunHeight, - foreground - ); - } - sprite.setTextColor(foreground); - sprite.drawText(score, 235, 5); - sprite.pushSprite(); - - // Collision detection - if ( - 20 < obstacleX + obstacle.width && - 40 > obstacleX && - dinoY + dinoRunHeight - 20 /* dinoBottom */ > obstacleY - ) { - // Game over - if (obstacleY < 78) { - if (obstacleY < 50) { - if (!dinoIsJumping) continue; - } else { - if (dinoIsDucking) continue; - } - } - - audio.tone(60, 100); // 50 - delay(20); - audio.tone(60, 180); // 90 - display.setTextColor(foreground); - display.setTextSize(2); - display.setTextAlign(0); - display.drawText('GAME OVER', 70, 40); - delay(500); - while (!keyboard.getAnyPress()) { - delay(10); - } - - obstacleX = displayWidth + 50; - delay(500); - startTime = now(); - } - } - keyboard.setLongPress(false); -} -main(); +var display = require('display'); +var keyboard = require('keyboard'); +__allow("audio"); +var audio = require('audio'); + +// I am putting all code in function to optimise, if variables are outside +// functions they are put in global namespace, and it's slower to get +// But if you store too much in one function you can cause stackoverflow +function main() { + // This is XBM format, you can convert images to this format using: + // https://www.online-utility.org/image/convert/to/XBM + // dinoRun: 40x43 + var dinoRunWidth = 40; + var dinoRunHeight = 43; + var dinoRunSprite = [ + new Uint8Array([ + 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, + 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xfc, 0xff, 0x00, 0x00, 0xf0, 0xfc, + 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, + 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, + 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0x3f, 0x00, + 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0x00, + 0xf0, 0xff, 0x0f, 0x03, 0x00, 0xfc, 0x0f, 0x00, 0x03, 0x00, 0xfc, 0x0f, + 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x0f, + 0xf0, 0xff, 0xff, 0x00, 0x0f, 0xf0, 0xff, 0xff, 0x00, 0x3f, 0xfc, 0xff, + 0xcf, 0x00, 0x3f, 0xfc, 0xff, 0xcf, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, + 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, + 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x03, + 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xc0, + 0xff, 0xff, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x00, + 0x00, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x3c, + 0x30, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, + ]), + new Uint8Array([ + 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, + 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xfc, 0xff, 0x00, 0x00, 0xf0, 0xfc, + 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, + 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, + 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0x3f, 0x00, + 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0x00, + 0xf0, 0xff, 0x0f, 0x03, 0x00, 0xfc, 0x0f, 0x00, 0x03, 0x00, 0xfc, 0x0f, + 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x0f, + 0xf0, 0xff, 0xff, 0x00, 0x0f, 0xf0, 0xff, 0xff, 0x00, 0x3f, 0xfc, 0xff, + 0xcf, 0x00, 0x3f, 0xfc, 0xff, 0xcf, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, + 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, + 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x03, + 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xc0, + 0xff, 0xff, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x3f, + 0x00, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x00, 0x00, + 0x00, 0xfc, 0x30, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x00, 0x00, 0x3c, + 0xf0, 0x00, 0x00, 0x00, 0x0c, 0xf0, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, + ]), + ]; + // dino_duck: 55x26 + var dinoDuckSprite = [ + new Uint8Array([ + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xe0, + 0xff, 0x1f, 0x3f, 0xc0, 0xff, 0x7f, 0xe0, 0xff, 0x1f, 0x3f, 0xc0, 0xff, + 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x1f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xe1, 0xff, 0x07, 0x00, 0xfc, 0xff, + 0xff, 0xe1, 0xff, 0x07, 0x00, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x7e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, + 0x00, 0x00, + ]), + new Uint8Array([ + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xe0, + 0xff, 0x1f, 0x3f, 0xc0, 0xff, 0x7f, 0xe0, 0xff, 0x1f, 0x3f, 0xc0, 0xff, + 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xff, + 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x00, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0x1f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xe1, 0xff, 0x07, 0x00, 0xfc, 0xff, + 0xff, 0xe1, 0xff, 0x07, 0x00, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xe1, 0x63, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xe1, 0xe3, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, + 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, + ]), + ]; + // ground: 623x12 + var groundSprite = new Uint8Array([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x60, + 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, + 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x70, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x60, 0x00, 0x00, 0x00, 0x80, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]); + var bird1Sprite = new Uint8Array([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, + 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, + 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, + 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + ]); + var bird2Sprite = new Uint8Array([ + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3f, 0x00, 0x00, 0x00, + 0xc0, 0x0f, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xff, 0x00, 0x00, 0x00, + 0xf0, 0x3f, 0xff, 0x03, 0x00, 0x00, 0xf0, 0x3f, 0xff, 0x03, 0x00, 0x00, + 0xfc, 0x3f, 0xff, 0x0f, 0x00, 0x00, 0xfc, 0x3f, 0xff, 0x0f, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, + 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, + 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, + 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xf0, 0xff, 0x03, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]); + var cactusSmallSprite = new Uint8Array([ + 0xc0, 0x01, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x23, 0xe0, 0x73, + 0xe0, 0x73, 0xe0, 0x73, 0xe2, 0x73, 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x73, + 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x7f, 0xe7, 0x3f, 0xe7, 0x1f, + 0xe7, 0x03, 0xff, 0x03, 0xfe, 0x03, 0xfc, 0x03, 0xe0, 0x03, 0xe0, 0x03, + 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, + 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, + ]); + var cactusBigSprite = new Uint8Array([ + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x0e, 0x7f, 0x00, 0x1f, 0x7f, 0x00, + 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x38, + 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, + 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, + 0x1f, 0x7f, 0x7c, 0xff, 0x7f, 0x7c, 0xfe, 0x7f, 0x7c, 0xfc, 0x7f, 0x7c, + 0xf8, 0x7f, 0x7c, 0xf0, 0x7f, 0x7c, 0x00, 0x7f, 0x7c, 0x00, 0xff, 0x3f, + 0x00, 0xff, 0x1f, 0x00, 0xff, 0x0f, 0x00, 0xff, 0x07, 0x00, 0x7f, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, + 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x04, 0xd0, 0x7f, 0x00, + ]); + // cloud: 46x13 + var cloudSprite = new Uint8Array([ + 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x73, 0x00, 0x00, + 0x00, 0x00, 0x18, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x80, 0x07, 0x00, 0x00, 0x00, 0x02, 0x80, 0xfc, 0x00, + 0x00, 0xc0, 0x03, 0x40, 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x07, + 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1c, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x02, 0x00, 0x00, 0x00, 0x20, + 0x03, 0xfc, 0xff, 0xff, 0xff, 0x3f, + ]); + var obstacles = [ + { + isAnimated: true, + spawnsY: [88, 62, 45], + width: 42, + height: 36, + sprites: [bird1Sprite, bird2Sprite], + }, + { + isAnimated: false, + spawnsY: [92], + width: 15, + height: 33, + sprites: [cactusSmallSprite], + }, + { + isAnimated: false, + spawnsY: [78], + width: 23, + height: 47, + sprites: [cactusBigSprite], + }, + ]; + var black = display.color(0, 0, 0); + var white = display.color(255, 255, 255); + var grey = display.color(100, 100, 100); + var gravity = 700; + + // Dino properties + var dinoY = 82; // Initial position + var dinoVelocity = 0; + var dinoJumpStrength = -350; + var dinoIsJumping = false; + var dinoIsDucking = false; + + // Obstacle properties + var obstacleX = 300; // Start off-screen + var obstacle = obstacles[1]; + var obstacleY = obstacle.spawnsY[0]; + + // Ground properties + var groundX = 0; // Track ground position + var groundSpeed = 4; // Speed of ground movement + var sprite = display.createSprite(); + + var deltaTime = 0; + var nowTime = now(); + var oldTime = nowTime; + var startTime = nowTime; + + var playPointSound = 0; + + var dayInterval = 700; + var dayTransitionRange = 10; + var baseColorValue = 0; + var baseColorInverted = Math.abs(baseColorValue - 255); + + var displayWidth = display.width(); + var displayHeight = display.height(); + + var clouds = [ + { x: random(displayWidth, displayWidth + 100), y: random(0, 50) }, + { x: random(displayWidth + 200, displayWidth + 300), y: random(0, 50) }, + { x: random(displayWidth + 400, displayWidth + 500), y: random(0, 50) } + ]; + var foreground = black; + var background = white; + + var score = 0; + + sprite.fill(background); + sprite.setTextColor(foreground); + sprite.setTextSize(2); + sprite.setTextAlign(2); + keyboard.setLongPress(true); + while (true) { + if (keyboard.getPrevPress(true)) { + break; // Exits the game when a prev button is pressed. + } + + var selPressed = keyboard.getSelPress(true); + if (selPressed && !dinoIsJumping && !dinoIsDucking) { + dinoVelocity = dinoJumpStrength; // Start the jump + dinoIsJumping = true; + audio.tone(494, 40, true); + } + + var nextPressed = keyboard.getNextPress(true) || keyboard.getEscPress(true); + if (nextPressed && !dinoIsJumping) { + dinoIsDucking = true; + } + + if (!nextPressed && dinoIsDucking) { + dinoIsDucking = false; + } + + nowTime = now(); + deltaTime = (nowTime - oldTime) / 1000; + oldTime = nowTime; + + score = Math.floor((nowTime - startTime) / 100); + + if (playPointSound === 0 && score % 100 < 10 && score > 50) { + playPointSound = 1; + audio.tone(784, 80, true); + } else if (playPointSound === 1) { + audio.tone(784, 220, true); + playPointSound = 2; + } else if (score % 100 > 50 && playPointSound === 2) { + playPointSound = 0; + } + + // Apply gravity + dinoVelocity += gravity * deltaTime; + dinoY += dinoVelocity * deltaTime; + + // Prevent dino from falling below ground + if (dinoY > 82) { + dinoY = 82; + dinoIsJumping = false; // Allow jumping again + dinoVelocity = 0; + } + + groundSpeed = 4 + Math.floor(score / 200); + // Move the ground + groundX -= groundSpeed; + if (groundX <= -383) { + groundX = 0; // Reset position for seamless looping + } + + // Move the obstacle + obstacleX -= groundSpeed; + // Reset obstacle + if (obstacleX < -obstacle.width) { + obstacleX = displayWidth + random(0, 100); // Random respawn + obstacle = obstacles[random(0, 3)]; + obstacleY = obstacle.spawnsY[random(0, obstacle.spawnsY.length)]; + } + + // Move clouds + for (var i = 0; i < 2; i++) { + clouds[i].x -= groundSpeed - 2; + if (clouds[i].x < -46) { + clouds[i].x = displayWidth + 50; + clouds[i].y = random(0, 50); + } + } + + // Day/night cycle + var modScore = score % (dayInterval * 2); + baseColorValue = 0; + baseColorInverted = 255; + if (modScore % dayInterval >= dayInterval - dayTransitionRange) { + baseColorValue = Math.round( + 255 * + (((modScore % dayInterval) - (dayInterval - dayTransitionRange)) / + dayTransitionRange) + ); + baseColorInverted = Math.abs(baseColorValue - 255); + } + if (modScore < dayInterval) { + foreground = display.color( + baseColorValue, + baseColorValue, + baseColorValue + ); + background = display.color( + baseColorInverted, + baseColorInverted, + baseColorInverted + ); + } else { + foreground = display.color( + baseColorInverted, + baseColorInverted, + baseColorInverted + ); + background = display.color( + baseColorValue, + baseColorValue, + baseColorValue + ); + } + + // Draw the scene + sprite.fill(background); + for (var i = 0; i < 2; i++) { + sprite.drawXBitmap(clouds[i].x, clouds[i].y, cloudSprite, 46, 13, grey); + } + sprite.drawXBitmap(groundX, 118, groundSprite, 623, 12, foreground); + if ((displayWidth > 240) && ((623 + groundX) < displayWidth)) { + sprite.drawXBitmap(623 + groundX, 118, groundSprite, 623, 12, foreground); + } + sprite.drawXBitmap( + obstacleX, + obstacleY, + obstacle.sprites[ + obstacle.isAnimated ? Math.floor((nowTime % 500) / 250) : 0 + ], + obstacle.width, + obstacle.height, + foreground + ); + if (dinoIsDucking) { + sprite.drawXBitmap( + 10, + dinoY + 17, + dinoDuckSprite[Math.floor((nowTime % 200) / 100)], + 55, + 26, + foreground + ); + } else { + sprite.drawXBitmap( + 10, + dinoY, + dinoRunSprite[Math.floor((nowTime % 200) / 100)], + dinoRunWidth, + dinoRunHeight, + foreground + ); + } + sprite.setTextColor(foreground); + sprite.drawText(score, 235, 5); + sprite.pushSprite(); + + // Collision detection + if ( + 20 < obstacleX + obstacle.width && + 40 > obstacleX && + dinoY + dinoRunHeight - 20 /* dinoBottom */ > obstacleY + ) { + // Game over + if (obstacleY < 78) { + if (obstacleY < 50) { + if (!dinoIsJumping) continue; + } else { + if (dinoIsDucking) continue; + } + } + + audio.tone(60, 100); // 50 + delay(20); + audio.tone(60, 180); // 90 + display.setTextColor(foreground); + display.setTextSize(2); + display.setTextAlign(0); + display.drawText('GAME OVER', 70, 40); + delay(500); + while (!keyboard.getAnyPress()) { + delay(10); + } + + obstacleX = displayWidth + 50; + delay(500); + startTime = now(); + } + } + keyboard.setLongPress(false); +} +main(); diff --git a/sd_files/interpreter/dtmf.js b/sd_files/interpreter/dtmf.js index 30dc3f9338..6fb3a75c7b 100644 --- a/sd_files/interpreter/dtmf.js +++ b/sd_files/interpreter/dtmf.js @@ -1,5 +1,6 @@ var display = require('display'); var keyboardApi = require('keyboard'); +__allow("audio"); var audio = require('audio'); var keyboardPrompt = keyboardApi.keyboard; diff --git a/sd_files/interpreter/gifs.js b/sd_files/interpreter/gifs.js index b97e0ca842..2424831ac3 100644 --- a/sd_files/interpreter/gifs.js +++ b/sd_files/interpreter/gifs.js @@ -1,4 +1,5 @@ var display = require('display'); +__allow("serial"); var serialApi = require('serial'); var println = display.println; @@ -12,48 +13,48 @@ function main() { const patrick = gifOpen('littlefs', '/gifs/half_patrick.gif'); const spongebob = gifOpen('littlefs', '/gifs/half_spongebob.gif'); const rick = gifOpen('littlefs', '/gifs/half_rick.gif'); - const rainbow = gifOpen('littlefs', '/gifs/half_rainbow.gif'); - - // Check if all GIFs failed to load - if (!patrick && !spongebob && !rick && !rainbow) { - println("Cannot load gifs"); - println("Make sure that they are in /gifs folder"); - delay(3000); - throw new Error("Cannot load gifs"); - } - - var elapsed; - var frames = 0; - var fps = 0; - var startTime = now(); - - while(true) { - patrick.playFrame(0, 0); // Plays a frame of the GIF at the specified position. - spongebob.playFrame(60, 0); - rick.playFrame(120, 0); - rainbow.playFrame(180, 0); - - frames++; - if (frames % 10 === 0) { - elapsed = (now() - startTime) / 1000; - fps = frames / elapsed; - serialPrintln(fps); - } - - // To remove gif you can use: - // const dimensions = rainbow.dimensions(); // Retrieves the dimensions of the Rainbow GIF. - // drawFillRect(x + 120, y, dimensions.width, dimensions.height, color(0, 0, 0)); // Clears the previous frame. - // rainbow.close(); // Closes the Rainbow GIF to release resources. - - // patrick.playFrame(x, y); - // You can move GIFs by changing x and y. Ensure you clear the old position - // with drawFillRect(x, y, dimensions.width, dimensions.height, color(0, 0, 0)) - // and patrick.reset() it because GIFs are often optimized to draw only the pixels that change between frames. - - if (getAnyPress()) { - break; // Exits the loop when a button is pressed. - } - } -} - + const rainbow = gifOpen('littlefs', '/gifs/half_rainbow.gif'); + + // Check if all GIFs failed to load + if (!patrick && !spongebob && !rick && !rainbow) { + println("Cannot load gifs"); + println("Make sure that they are in /gifs folder"); + delay(3000); + throw new Error("Cannot load gifs"); + } + + var elapsed; + var frames = 0; + var fps = 0; + var startTime = now(); + + while(true) { + patrick.playFrame(0, 0); // Plays a frame of the GIF at the specified position. + spongebob.playFrame(60, 0); + rick.playFrame(120, 0); + rainbow.playFrame(180, 0); + + frames++; + if (frames % 10 === 0) { + elapsed = (now() - startTime) / 1000; + fps = frames / elapsed; + serialPrintln(fps); + } + + // To remove gif you can use: + // const dimensions = rainbow.dimensions(); // Retrieves the dimensions of the Rainbow GIF. + // drawFillRect(x + 120, y, dimensions.width, dimensions.height, color(0, 0, 0)); // Clears the previous frame. + // rainbow.close(); // Closes the Rainbow GIF to release resources. + + // patrick.playFrame(x, y); + // You can move GIFs by changing x and y. Ensure you clear the old position + // with drawFillRect(x, y, dimensions.width, dimensions.height, color(0, 0, 0)) + // and patrick.reset() it because GIFs are often optimized to draw only the pixels that change between frames. + + if (getAnyPress()) { + break; // Exits the loop when a button is pressed. + } + } +} + main(); diff --git a/sd_files/interpreter/ir2keys.js b/sd_files/interpreter/ir2keys.js index a0fd6acfa1..127801b3a5 100644 --- a/sd_files/interpreter/ir2keys.js +++ b/sd_files/interpreter/ir2keys.js @@ -1,7 +1,10 @@ var display = require('display'); var keyboard = require('keyboard'); +__allow("ir"); var ir = require('ir'); +__allow("serial"); var serial = require('serial'); +__allow("badusb"); var badusb = require('badusb'); var fillScreen = display.fill; @@ -19,61 +22,61 @@ var badusbPrint = badusb.print; var badusbPrintln = badusb.println; var badusbPressRaw = badusb.pressRaw; var badusbRunFile = badusb.runFile; - -// flirc-like IR2Keyboard script -// use any IR remote to send commands to your pc -// requires: IR receiver + USBHID-capable board - -var running=true; -badusbSetup(); - -while(true) { - fillScreen(0); - drawString("waiting for IR signals...", 3 , 0); - print("waiting for IR signals...", 0 , 0); - - if(getAnyPress()) break; - var cmd = serialReadln(1); - if(cmd.trim()!="") break; - - var curr_ir_signal = irRead(1); // 1s timeout - - drawString(curr_ir_signal, 3 , 16); - drawString("hold any key to stop", 3 , 32); - - print("received:"); - print(curr_ir_signal); - - if(curr_ir_signal) { - - delay(delay_ms); - fillScreen(0); - - // switch on curr_ir_signal - // https://github.com/espressif/arduino-esp32/blob/master/libraries/USB/src/USBHIDConsumerControl.h - if(curr_ir_signal.includes("address: 04 00 00 00\ncommand: B0 00 00 00")) { // CUSTOMIZE FOR YOUR REMOTE - print("pressed play"); - //badusbPress(0x20); // KEY_SPACE - badusbPress(0x2c); // KEY_SPACE - //badusbPressSpecial(0x00CD); // HID_USAGE_CONSUMER_PLAY_PAUSE - } - else if(curr_ir_signal.includes("address: 04 00 00 00\ncommand: 8E 00 00 00")) { // CUSTOMIZE FOR YOUR REMOTE - print("pressed next"); - badusbPrint("n"); // vlc next shortcut - badusbPressRaw(0x11); // HID_KEY_N - //badusbPressSpecial(0x00B5); // HID_USAGE_CONSUMER_SCAN_NEXT - } - else if(curr_ir_signal.includes("address: 04 00 00 00\ncommand: 8F 00 00 00")) { // CUSTOMIZE FOR YOUR REMOTE - print("pressed prev"); - badusbPrint("p"); // vlc next shortcut - badusbPressRaw(0x13); // HID_KEY_P - //badusbPressSpecial(0x00B6); // HID_USAGE_CONSUMER_SCAN_PREVIOUS - } - else if(curr_ir_signal.includes("address: 04 00 00 00\ncommand: 8F 00 00 00")) { // CUSTOMIZE FOR YOUR REMOTE - print("pressed stop"); - //badusbPressSpecial(0x00B7); // HID_USAGE_CONSUMER_STOP - } - else - print("unknown button pressed"); - } -} + +// flirc-like IR2Keyboard script +// use any IR remote to send commands to your pc +// requires: IR receiver + USBHID-capable board + +var running=true; +badusbSetup(); + +while(true) { + fillScreen(0); + drawString("waiting for IR signals...", 3 , 0); + print("waiting for IR signals...", 0 , 0); + + if(getAnyPress()) break; + var cmd = serialReadln(1); + if(cmd.trim()!="") break; + + var curr_ir_signal = irRead(1); // 1s timeout + + drawString(curr_ir_signal, 3 , 16); + drawString("hold any key to stop", 3 , 32); + + print("received:"); + print(curr_ir_signal); + + if(curr_ir_signal) { + + delay(delay_ms); + fillScreen(0); + + // switch on curr_ir_signal + // https://github.com/espressif/arduino-esp32/blob/master/libraries/USB/src/USBHIDConsumerControl.h + if(curr_ir_signal.includes("address: 04 00 00 00\ncommand: B0 00 00 00")) { // CUSTOMIZE FOR YOUR REMOTE + print("pressed play"); + //badusbPress(0x20); // KEY_SPACE + badusbPress(0x2c); // KEY_SPACE + //badusbPressSpecial(0x00CD); // HID_USAGE_CONSUMER_PLAY_PAUSE + } + else if(curr_ir_signal.includes("address: 04 00 00 00\ncommand: 8E 00 00 00")) { // CUSTOMIZE FOR YOUR REMOTE + print("pressed next"); + badusbPrint("n"); // vlc next shortcut + badusbPressRaw(0x11); // HID_KEY_N + //badusbPressSpecial(0x00B5); // HID_USAGE_CONSUMER_SCAN_NEXT + } + else if(curr_ir_signal.includes("address: 04 00 00 00\ncommand: 8F 00 00 00")) { // CUSTOMIZE FOR YOUR REMOTE + print("pressed prev"); + badusbPrint("p"); // vlc next shortcut + badusbPressRaw(0x13); // HID_KEY_P + //badusbPressSpecial(0x00B6); // HID_USAGE_CONSUMER_SCAN_PREVIOUS + } + else if(curr_ir_signal.includes("address: 04 00 00 00\ncommand: 8F 00 00 00")) { // CUSTOMIZE FOR YOUR REMOTE + print("pressed stop"); + //badusbPressSpecial(0x00B7); // HID_USAGE_CONSUMER_STOP + } + else + print("unknown button pressed"); + } +} diff --git a/sd_files/interpreter/ir_brute.js b/sd_files/interpreter/ir_brute.js index 61bd7152e1..bc5d67b816 100644 --- a/sd_files/interpreter/ir_brute.js +++ b/sd_files/interpreter/ir_brute.js @@ -1,6 +1,7 @@ var display = require('display'); var keyboardApi = require('keyboard'); var dialog = require('dialog'); +__allow("ir"); var ir = require('ir'); var fillScreen = display.fill; diff --git a/sd_files/interpreter/rf_brute.js b/sd_files/interpreter/rf_brute.js index 3f29a5caef..d68b1e5b3a 100644 --- a/sd_files/interpreter/rf_brute.js +++ b/sd_files/interpreter/rf_brute.js @@ -1,6 +1,7 @@ var display = require('display'); var keyboardApi = require('keyboard'); var dialog = require('dialog'); +__allow("subghz"); var subghz = require('subghz'); var fillScreen = display.fill; diff --git a/sd_files/interpreter/tamagochi.js b/sd_files/interpreter/tamagochi.js index ba63910a7d..fcc32689dc 100644 --- a/sd_files/interpreter/tamagochi.js +++ b/sd_files/interpreter/tamagochi.js @@ -1,10 +1,12 @@ // created by hxd57. V2 // repository: https://github.com/HawkstoNGriM/m5-bruce-tamagochi-lite var dbStore = {fs: "sd", path: "/pet.json"}; // fs can be "sd" or "littlefs" +__allow("storage"); var storage = require("storage"); var display = require('display'); var keyboard = require('keyboard'); var dialog = require('dialog'); +__allow("serial"); var serialApi = require('serial'); var width = display.width; diff --git a/sd_files/interpreter/timer_background.js b/sd_files/interpreter/timer_background.js index ea666ace05..0f18e66b04 100644 --- a/sd_files/interpreter/timer_background.js +++ b/sd_files/interpreter/timer_background.js @@ -1,7 +1,9 @@ var runtime = require("runtime"); +__allow("audio"); var audio = require("audio"); var display = require("display"); var keyboard = require("keyboard"); +__allow("gpio"); var gpio = require("gpio"); var alarmTime = 0; diff --git a/sd_files/interpreter/wifi_brute.js b/sd_files/interpreter/wifi_brute.js index 475a7055c6..9cce0c2a75 100644 --- a/sd_files/interpreter/wifi_brute.js +++ b/sd_files/interpreter/wifi_brute.js @@ -1,8 +1,11 @@ var dialog = require('dialog'); +__allow("wifi"); var wifi = require('wifi'); +__allow("storage"); var storage = require('storage'); var keyboard = require('keyboard'); var display = require('display'); +__allow("serial"); var serial = require('serial'); var dialogMessage = dialog.info; diff --git a/sd_files/xFlipper.js b/sd_files/xFlipper.js index b7873c177b..1679ef64c0 100644 --- a/sd_files/xFlipper.js +++ b/sd_files/xFlipper.js @@ -14,6 +14,7 @@ let icon = require("gui/icon"); let flipper = require("flipper"); let press =0; +__allow("serial"); let serial = require("serial"); serial.setup("usart", 115200); diff --git a/src/modules/bjs_interpreter/globals_js.cpp b/src/modules/bjs_interpreter/globals_js.cpp index 6e41ae8ded..5ee8bf5ce7 100644 --- a/src/modules/bjs_interpreter/globals_js.cpp +++ b/src/modules/bjs_interpreter/globals_js.cpp @@ -2,6 +2,23 @@ #include "globals_js.h" #include "user_classes_js.h" +// Safe modules always accessible via require() +static const char *SAFE_MODULES[] = { + "display", "dialog", "keyboard", "runtime", "notification", + "math", "device", "led", "menu", "event_loop", "gui", + "gui/loading", "gui/submenu", "gui/empty_screen", "gui/text_input", + "gui/byte_input", "gui/text_box", "gui/dialog", "gui/file_picker", + "gui/icon", "flipper", "buffer" +}; +static const int NUM_SAFE_MODULES = sizeof(SAFE_MODULES) / sizeof(SAFE_MODULES[0]); + +// Dangerous modules that require explicit __allow() permission +static const char *DANGEROUS_MODULES[] = { + "storage", "wifi", "subghz", "nrf24", "ble", "ir", + "badusb", "gpio", "i2c", "rfid", "serial", "mic", "audio" +}; +static const int NUM_DANGEROUS_MODULES = sizeof(DANGEROUS_MODULES) / sizeof(DANGEROUS_MODULES[0]); + #include "mbedtls/base64.h" JSValue js_gc(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { @@ -302,12 +319,89 @@ JSValue native_require(JSContext *ctx, JSValue *this_val, int argc, JSValue *arg const char *name = JS_ToCString(ctx, argv[0], &name_buf); if (!name) { return JS_EXCEPTION; } + // Check if module is safe (always allowed) + bool is_safe = false; + for (int i = 0; i < NUM_SAFE_MODULES; i++) { + if (strcmp(name, SAFE_MODULES[i]) == 0) { is_safe = true; break; } + } + + // Check if module is dangerous (needs permission) + bool is_dangerous = false; + for (int i = 0; i < NUM_DANGEROUS_MODULES; i++) { + if (strcmp(name, DANGEROUS_MODULES[i]) == 0) { is_dangerous = true; break; } + } + JSValue global = JS_GetGlobalObject(ctx); - JSValue val = JS_GetPropertyStr(ctx, global, name); + + // For dangerous modules, check __permitted_modules set + if (is_dangerous) { + JSValue permitted = JS_GetPropertyStr(ctx, global, "__permitted_modules"); + bool has_permission = false; + if (JS_IsObject(permitted)) { + JSValue perm_val = JS_GetPropertyStr(ctx, permitted, name); + has_permission = JS_ToBool(ctx, perm_val); + JS_FreeValue(ctx, perm_val); + } + JS_FreeValue(ctx, permitted); + + if (!has_permission) { + JS_FreeValue(ctx, global); + return JS_ThrowTypeError( + ctx, + "Module '%s' requires permission. Call __allow('%s') first.", + name, name + ); + } + } + + // Look up module from __module_registry (hidden internal store) + JSValue registry = JS_GetPropertyStr(ctx, global, "__module_registry"); + JSValue val; + if (JS_IsObject(registry)) { + val = JS_GetPropertyStr(ctx, registry, name); + } else { + // Fallback: direct global lookup (for modules not in registry) + val = JS_GetPropertyStr(ctx, global, name); + } + JS_FreeValue(ctx, registry); + JS_FreeValue(ctx, global); return val; } +JSValue native_requireAllow(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { + if (argc < 1) { return JS_ThrowTypeError(ctx, "__allow() expects 1 argument"); } + + JSCStringBuf name_buf; + const char *name = JS_ToCString(ctx, argv[0], &name_buf); + if (!name) { return JS_EXCEPTION; } + + // Validate that the module name is in the dangerous list + bool valid = false; + for (int i = 0; i < NUM_DANGEROUS_MODULES; i++) { + if (strcmp(name, DANGEROUS_MODULES[i]) == 0) { valid = true; break; } + } + if (!valid) { + return JS_ThrowTypeError(ctx, "Unknown or unrestricted module: '%s'", name); + } + + JSValue global = JS_GetGlobalObject(ctx); + + // Get or create permitted_modules set + JSValue permitted = JS_GetPropertyStr(ctx, global, "__permitted_modules"); + if (!JS_IsObject(permitted)) { + permitted = JS_NewObject(ctx); + } + + JS_SetPropertyStr(ctx, permitted, name, JS_NewBool(ctx, 1)); + JS_SetPropertyStr(ctx, global, "__permitted_modules", permitted); + + JS_FreeValue(ctx, permitted); + JS_FreeValue(ctx, global); + + return JS_NewBool(ctx, 1); +} + JSValue native_assert(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv) { bool ok = false; if (argc > 0) ok = JS_ToBool(ctx, argv[0]); diff --git a/src/modules/bjs_interpreter/globals_js.h b/src/modules/bjs_interpreter/globals_js.h index a0a611c87c..62a6918674 100644 --- a/src/modules/bjs_interpreter/globals_js.h +++ b/src/modules/bjs_interpreter/globals_js.h @@ -27,6 +27,7 @@ JSValue js_date_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); JSValue js_performance_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); JSValue native_require(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); +JSValue native_requireAllow(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); JSValue native_assert(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); JSValue native_now(JSContext *ctx, JSValue *this_val, int argc, JSValue *argv); diff --git a/src/modules/bjs_interpreter/interpreter.cpp b/src/modules/bjs_interpreter/interpreter.cpp index 62be9ef31a..0505467a7b 100644 --- a/src/modules/bjs_interpreter/interpreter.cpp +++ b/src/modules/bjs_interpreter/interpreter.cpp @@ -62,6 +62,43 @@ void interpreterHandler(void *pvParameters) { JSContext *ctx = JS_NewContext(mem_buf, mem_size, &js_stdlib); JS_SetLogFunc(ctx, js_log_func); + // Build hidden module registry and remove dangerous modules from global scope + // This ensures require() is the ONLY way to access dangerous hardware modules + JSValue global_obj = JS_GetGlobalObject(ctx); + + // List of all modules to store in hidden registry + const char *MODULE_NAMES[] = { + "audio", "badusb", "ble", "device", "display", "dialog", + "gpio", "i2c", "ir", "keyboard", "led", "menu", "mic", + "notification", "nrf24", "rfid", "runtime", "serial", + "storage", "subghz", "wifi", "event_loop", "gui", + "gui/loading", "gui/submenu", "gui/empty_screen", "gui/text_input", + "gui/byte_input", "gui/text_box", "gui/dialog", "gui/file_picker", + "gui/icon", "flipper" + }; + + JSValue module_registry = JS_NewObject(ctx); + for (size_t i = 0; i < sizeof(MODULE_NAMES)/sizeof(MODULE_NAMES[0]); i++) { + JSValue mod = JS_GetPropertyStr(ctx, global_obj, MODULE_NAMES[i]); + if (!JS_IsUndefined(mod)) { + JS_SetPropertyStr(ctx, module_registry, MODULE_NAMES[i], mod); + } + JS_FreeValue(ctx, mod); + } + + // Dangerous modules to remove from global scope + const char *RESTRICTED_MODULES[] = { + "storage", "wifi", "subghz", "nrf24", "ble", "ir", + "badusb", "gpio", "i2c", "rfid", "serial", "mic", "audio" + }; + for (size_t i = 0; i < sizeof(RESTRICTED_MODULES)/sizeof(RESTRICTED_MODULES[0]); i++) { + JS_DeleteProperty(ctx, global_obj, JS_NewAtom(ctx, RESTRICTED_MODULES[i]), 0); + } + + JS_SetPropertyStr(ctx, global_obj, "__module_registry", module_registry); + JS_FreeValue(ctx, module_registry); + JS_FreeValue(ctx, global_obj); + js_timers_init(ctx); // Set global variables @@ -74,6 +111,7 @@ void interpreterHandler(void *pvParameters) { JS_SetPropertyStr(ctx, global, "BRUCE_PRICOLOR", JS_NewInt32(ctx, bruceConfig.priColor)); JS_SetPropertyStr(ctx, global, "BRUCE_SECCOLOR", JS_NewInt32(ctx, bruceConfig.secColor)); JS_SetPropertyStr(ctx, global, "BRUCE_BGCOLOR", JS_NewInt32(ctx, bruceConfig.bgColor)); + JS_SetPropertyStr(ctx, global, "__allow", JS_NewCFunction(ctx, native_requireAllow, "__allow", 1)); JS_SetPropertyStr(ctx, global, "HIGH", JS_NewInt32(ctx, HIGH)); JS_SetPropertyStr(ctx, global, "LOW", JS_NewInt32(ctx, LOW));