[
  {
    "path": ".gitignore",
    "content": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n## Build generated\nbuild/\nDerivedData/\n\n## Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata/\n\n## Other\n*.moved-aside\n*.xcuserstate\n\n## Obj-C/Swift specific\n*.hmap\n*.ipa\n*.dSYM.zip\n*.dSYM\n\n# CocoaPods\n#\n# We recommend against adding the Pods directory to your .gitignore. However\n# you should judge for yourself, the pros and cons are mentioned at:\n# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control\n#\n# Pods/\n\n# Carthage\n#\n# Add this line if you want to avoid checking in source code from Carthage dependencies.\n# Carthage/Checkouts\n\nCarthage/Build\n\n# fastlane\n#\n# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the \n# screenshots whenever they are needed.\n# For more information about the recommended setup visit:\n# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md\n\nfastlane/report.xml\nfastlane/screenshots\n\n#Code Injection\n#\n# After new code Injection tools there's a generated folder /iOSInjectionProject\n# https://github.com/johnno1962/injectionforxcode\n\niOSInjectionProject/\nplatform/Windows/LowRes NX Win/Debug/\nplatform/Windows/LowRes NX Win/Release/\nplatform/Linux/output/\nplatform/Windows/Debug/\nplatform/Windows/Release/\nplatform/Windows/LowRes NX Win/SDL2.dll\nplatform/Windows/.vs/\n*.user\n*.aps\n*.bc\nplatform/web/output\nDisk.nx\n*.o\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "content": "# DESCRIPTION: GitLab CI/CD for libRetro\n\n##############################################################################\n################################# BOILERPLATE ################################\n##############################################################################\n\n# Core definitions\n.core-defs:\n  variables:\n    CORENAME: lowresnx\n    MAKEFILE_PATH: platform/LibRetro\n    JNI_PATH: platform/LibRetro\n\n# Inclusion templates, required for the build to work\ninclude:\n  ################################## DESKTOPS ################################\n  # Windows 64-bit\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/windows-x64-mingw.yml'\n\n  # Windows 32-bit\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/windows-i686-mingw.yml'\n\n  # Linux 64-bit\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/linux-x64.yml'\n    \n  # Linux 32-bit\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/linux-i686.yml'\n\n  # Linux 64-bit (ARM)\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/linux-aarch64.yml'\n\n  # MacOS PowerPC 32-bit\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/osx-ppc.yml'\n\n  # MacOS 64-bit\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/osx-x64.yml'\n\n  # MacOS ARM 64-bit\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/osx-arm64.yml'\n\n  # DJGPP\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/djgpp-static.yml'\n\n  ################################## CELLULAR ################################\n  # Android\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/android-jni.yml'\n    \n  # iOS\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/ios-arm64.yml'\n\n  # iOS (armv7)\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/ios9.yml'\n\n  ################################## CONSOLES ################################\n  # PlayStation Portable\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/psp-static.yml'\n\n  # PlayStation Vita\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/vita-static.yml'\n\n  # PlayStation2\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/ps2-static.yml'\n\n  # Nintendo 3DS\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/ctr-static.yml'\n\n  # Nintendo GameCube\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/ngc-static.yml'\n\n  # Nintendo Wii\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/wii-static.yml'\n\n  # Nintendo WiiU\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/wiiu-static.yml'\n\n  # Nintendo Switch\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/libnx-static.yml'\n\n  # tvOS (AppleTV)\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/tvos-arm64.yml'\n\n  # OpenDingux\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/dingux-mips32.yml'\n\n  # OpenDingux (ARM)\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/dingux-arm32.yml'\n\n  #################################### MISC ##################################\n  # Emscripten\n  - project: 'libretro-infrastructure/ci-templates'\n    file: '/emscripten-static.yml'\n\n# Stages for building\nstages:\n  - build-prepare\n  - build-shared\n  - build-static\n\n##############################################################################\n#################################### STAGES ##################################\n##############################################################################\n#\n################################### DESKTOPS #################################\n# Windows 64-bit\nlibretro-build-windows-x64:\n  extends:\n    - .libretro-windows-x64-mingw-make-default\n    - .core-defs\n\n# Windows 32-bit\nlibretro-build-windows-i686:\n  extends:\n    - .libretro-windows-i686-mingw-make-default\n    - .core-defs\n\n# Linux 64-bit\nlibretro-build-linux-x64:\n  extends:\n    - .libretro-linux-x64-make-default\n    - .core-defs\n    \n# Linux 32-bit\nlibretro-build-linux-i686:\n  extends:\n    - .libretro-linux-i686-make-default\n    - .core-defs\n\n# Linux 64-bit (ARM)\nlibretro-build-linux-aarch64:\n  extends:\n    - .libretro-linux-aarch64-make-default\n    - .core-defs\n\n# DJGPP\nlibretro-build-djgpp-i586:\n  extends:\n    - .libretro-djgpp-static-retroarch-master\n    - .core-defs\n\n# MacOS 64-bit\nlibretro-build-osx-x64:\n  extends:\n    - .libretro-osx-x64-make-default\n    - .core-defs\n\n# MacOS ARM 64-bit\nlibretro-build-osx-arm64:\n  extends:\n    - .libretro-osx-arm64-make-default\n    - .core-defs\n\n# MacOS PowerPC 32-bit\nlibretro-build-osx-ppc:\n  extends:\n    - .libretro-osx-ppc-make-default\n    - .core-defs\n\n################################### CELLULAR #################################\n# Android ARMv7a\nandroid-armeabi-v7a:\n  extends:\n    - .libretro-android-jni-armeabi-v7a\n    - .core-defs\n\n# Android ARMv8a\nandroid-arm64-v8a:\n  extends:\n    - .libretro-android-jni-arm64-v8a\n    - .core-defs\n\n# Android 64-bit x86\nandroid-x86_64:\n  extends:\n    - .libretro-android-jni-x86_64\n    - .core-defs\n    \n# Android 32-bit x86\nandroid-x86:\n  extends:\n    - .libretro-android-jni-x86\n    - .core-defs\n\n# iOS\nlibretro-build-ios-arm64:\n  extends:\n    - .libretro-ios-arm64-make-default\n    - .core-defs\n\n# iOS (armv7) [iOS 9 and up]\nlibretro-build-ios9:\n  extends:\n    - .libretro-ios9-make-default\n    - .core-defs\n    \n# tvOS\nlibretro-build-tvos-arm64:\n  extends:\n    - .libretro-tvos-arm64-make-default\n    - .core-defs\n\n################################### CONSOLES #################################\n# PlayStation Portable\nlibretro-build-psp:\n  extends:\n    - .libretro-psp-static-retroarch-master\n    - .core-defs\n\n# PlayStation Vita\nlibretro-build-vita:\n  extends:\n    - .libretro-vita-static-retroarch-master\n    - .core-defs\n\n# PlayStation2\nlibretro-build-ps2:\n  extends:\n    - .libretro-ps2-static-retroarch-master\n    - .core-defs\n\n# Nintendo 3DS\nlibretro-build-ctr:\n  extends:\n    - .libretro-ctr-static-retroarch-master\n    - .core-defs\n\n# Nintendo GameCube\nlibretro-build-ngc:\n  extends:\n    - .libretro-ngc-static-retroarch-master\n    - .core-defs\n\n# Nintendo Wii\nlibretro-build-wii:\n  extends:\n    - .libretro-wii-static-retroarch-master\n    - .core-defs\n\n# Nintendo WiiU\nlibretro-build-wiiu:\n  extends:\n    - .libretro-wiiu-static-retroarch-master\n    - .core-defs\n\n# Nintendo Switch\nlibretro-build-libnx-aarch64:\n  extends:\n    - .libretro-libnx-static-retroarch-master\n    - .core-defs\n\n# OpenDingux\nlibretro-build-dingux-mips32:\n  extends:\n    - .libretro-dingux-mips32-make-default\n    - .core-defs\n\n# OpenDingux Beta\nlibretro-build-dingux-odbeta-mips32:\n  extends:\n    - .libretro-dingux-odbeta-mips32-make-default\n    - .core-defs\n\n# OpenDingux Beta\nlibretro-build-rs90-odbeta-mips32:\n  extends:\n    - .libretro-rs90-odbeta-mips32-make-default\n    - .core-defs\n\n# RetroFW\nlibretro-build-retrofw-mips32:\n  extends:\n    - .libretro-retrofw-mips32-make-default\n    - .core-defs\n\n# Miyoo\nlibretro-build-miyoo-arm32:\n  extends:\n    - .libretro-miyoo-arm32-make-default\n    - .core-defs\n\n#################################### MISC ##################################\n# Emscripten\nlibretro-build-emscripten:\n  extends:\n    - .libretro-emscripten-static-retroarch-master\n    - .core-defs\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2016-2021 Timo Kloss\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n"
  },
  {
    "path": "README.md",
    "content": "# LowRes NX\n\n## Program retro games in BASIC\n\nMake your own retro games on a virtual game console. Program in the classic BASIC language and create sprites, tile maps, sound and music with the included tools. As a beginner you will quickly understand how to create simple text games or show your first sprite on a tile map. As an experienced programmer you can discover the full potential of retro hardware tricks!\n\n## Virtual Game Console\n\nImagine LowRes NX as a handheld game console with a d-pad, two action buttons and a little rubber keyboard below a slidable touchscreen. LowRes NX was inspired by real 8- and 16-bit systems and simulates chips for graphics, sound and I/O, which actually work like classic hardware. It supports hardware sprites as well as hardware parallax scrolling, and even offers vertical blank and raster interrupts to create authentic retro effects.\n\n## Old-School Programming\n\nThe programming language of LowRes NX is based on second-generation, structured BASIC. It offers all the classic commands, but with labels, loops and subprograms instead of line numbers. Graphics and sound are supported by additional commands and you can even access the virtual hardware directly using PEEK and POKE. You have complete control over the program flow, there is no standard update function to implement.\n\n## Creative Tools\n\nLowRes NX includes all the tools you need: The Gfx Designer for editing sprites, tiles, fonts and maps, as well as the Sound Composer for music and sound effects. All of these are just normal BASIC programs. You can change and improve them or even create your own custom editors.\n\n## Share and Play\n\nSend your games directly to other users or share them via the website. All programs are open source, so you can play them, learn from them and edit them. Do you prefer making just art or music? Share your creations as assets and let other programmers use them in their projects.\n"
  },
  {
    "path": "assets/overlay.nx",
    "content": "#2:CHARS\n00000000000000000000000000000000\n3C3C3C3C3C3C3C3C3C242424243C243C\nFEFEFEFE7E000000FE9292DA7E000000\n7EFFFFFFFFFFFF7E7EDB81DBDB81DB7E\n1C7F7F7F7F7F7F1C1C7741477141771C\nF7FFFFFE7FFFFFEFF79D9BF66FD9B9EF\n3E7E7EFFFFFFFF7F3E624AC7919BC57F\n3C3C7C7C780000003C24644C78000000\n1E3E7E7C7C7E3E1E1E32664C4C66321E\n787C7E3E3E7E7C78784C663232664C78\n007E7EFFFFFF7E7E007E5AE781E75A7E\n003C3CFFFFFF3C3C003C24E781E7243C\n0000003C3C7C7C780000003C24644C78\n000000FFFFFF0000000000FF81FF0000\n000000003C3C3C3C000000003C24243C\n0F1F3F7EFCF8F0E00F193366CC98B0E0\n7EFFFFFFFFFFFF7E7EC399918999C37E\n3C7C7C7C3CFFFFFF3C64446424E781FF\n7EFFFFFF7EFFFFFF7EC399F366CF81FF\n7EFFFFFFFFFFFF7E7EC399F3F999C37E\nFFFFFFFFFF0F0F0FFF999981F909090F\nFFFFFFFFFFFFFFFEFF819F83F9F983FE\n3E7EFEFFFFFFFF7E3E62CE839999C37E\nFFFFFF3F7E7C7878FF81F933664C4878\n7EFFFFFFFFFFFF7E7EC399C39999C37E\n7EFFFFFFFFFFFF7E7EC399C1F999C37E\n00003C3C3C3C3C0000003C243C243C00\n00003C3C3C7C7C7800003C243C644C78\n001E3E7E7C7E3E1E001E32664C66321E\n0000FFFFFFFFFF000000FF81FF81FF00\n00787C7E3E7E7C7800784C6632664C78\n7EFFFFFF3E3C3C3C7EC399F3263C243C\n7EFFFFFFFFFFFE7E7EC39991919FC27E\n3C7EFFFFFFFFFFFF3C66C399819999FF\nFEFFFFFFFFFFFFFEFE839983999983FE\n7EFFFFFFFFFFFF7E7EC3999F9F99C37E\nFCFEFFFFFFFFFEFCFC869399999386FC\nFFFFFFFCFCFFFFFFFF819F849C9F81FF\nFFFFFFFCFCF0F0F0FF819F849C9090F0\n7EFEFFFFFFFFFF7E7EC29F919999C37E\nFFFFFFFFFFFFFFFFFF999981999999FF\n7E7E7E3C3C7E7E7E7E4266242466427E\n3F3F3F0FFFFFFF7E3F213909F999C37E\nFFFFFFFEFEFFFFFFFF999386869399FF\nF0F0F0F0F0FFFFFFF0909090909F81FF\nE7FFFFFFFFFFFFFFE7BD9981819999FF\nFFFFFFFFFFFFFFFFFF998981919999FF\n7EFFFFFFFFFFFF7E7EC399999999C37E\nFEFFFFFFFEF0F0F0FE8399839E9090F0\n7EFFFFFFFFFFFF7F7EC399999593C17F\nFEFFFFFFFEFFFFFFFE839983869399FF\n7FFFFFFF7FFFFFFE7FC19FC379F983FE\nFFFFFF3C3C3C3C3CFF81E7242424243C\nFFFFFFFFFFFFFF7EFF9999999999C37E\nFFFFFFFFFFFF7E3CFF99999999C3663C\nFFFFFFFFFFFFFFE7FF9999818199BDE7\nFFFFFF7EFFFFFFFFFF99C366C39999FF\nFFFFFFFF7E3C3C3CFF9999C36624243C\nFFFFFF7EFCFFFFFFFF81F366CC9F81FF\n7EFFFFFFFFFFFF7E7EC399A5BDA5C37E\n7EFFFFFFFFFFFF7E7EC3A1B9A5B9C37E\n3C7EFFFFFFFF7E3C3C66E78181E7663C\n3C7EFFFFFF0000003C66C399FF000000\n0000000000FFFFFF0000000000FF81FF\n\n"
  },
  {
    "path": "core/accessories/disk_drive.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"disk_drive.h\"\n#include \"core.h\"\n#include <stdlib.h>\n#include <assert.h>\n#include <string.h>\n#include <stdint.h>\n\nvoid disk_init(struct Core *core)\n{\n    // init lazily in disk_prepare()\n}\n\nvoid disk_deinit(struct Core *core)\n{\n    struct DataManager *dataManager = &core->diskDrive->dataManager;\n    if (dataManager->data)\n    {\n        free(dataManager->data);\n        dataManager->data = NULL;\n    }\n    data_deinit(dataManager);\n}\n\nvoid disk_reset(struct Core *core)\n{\n    struct DataManager *dataManager = &core->diskDrive->dataManager;\n    if (dataManager->data)\n    {\n        data_reset(dataManager);\n    }\n}\n\nbool disk_prepare(struct Core *core)\n{\n    struct DataManager *dataManager = &core->diskDrive->dataManager;\n    if (dataManager->data == NULL)\n    {\n        dataManager->data = calloc(DATA_SIZE, 1);\n        if (!dataManager->data) exit(EXIT_FAILURE);\n        \n        data_init(dataManager);\n    }\n    return delegate_diskDriveWillAccess(core);\n}\n\nbool disk_saveFile(struct Core *core, int index, char *comment, int address, int length)\n{\n    if (!disk_prepare(core))\n    {\n        return false;\n    }\n    \n    assert(address >= 0 && address + length <= sizeof(struct Machine));\n    struct DataManager *dataManager = &core->diskDrive->dataManager;\n    if (!data_canSetEntry(dataManager, index, length))\n    {\n        delegate_diskDriveIsFull(core);\n    }\n    else\n    {\n        uint8_t *source = &((uint8_t *)core->machine)[address];\n        data_setEntry(dataManager, index, comment, source, length);\n        \n        delegate_diskDriveDidSave(core);\n    }\n    return true;\n}\n\nbool disk_loadFile(struct Core *core, int index, int address, int maxLength, int offset, bool *pokeFailed)\n{\n    if (!disk_prepare(core))\n    {\n        return false;\n    }\n    \n    struct DataEntry *entry = &core->diskDrive->dataManager.entries[index];\n    uint8_t *data = core->diskDrive->dataManager.data;\n    \n    // read file\n    int start = entry->start + offset;\n    int length = entry->length;\n    if (maxLength > 0 && length > maxLength)\n    {\n        length = maxLength;\n    }\n    if (offset + length > entry->length)\n    {\n        length = entry->length - offset;\n    }\n    for (int i = 0; i < length; i++)\n    {\n        bool poke = machine_poke(core, address + i, data[i + start]);\n        if (!poke)\n        {\n            *pokeFailed = true;\n            return true;\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "core/accessories/disk_drive.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef disk_drive_h\n#define disk_drive_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"data_manager.h\"\n\nstruct Core;\n\nstruct DiskDrive {\n    struct DataManager dataManager;\n};\n\nvoid disk_init(struct Core *core);\nvoid disk_deinit(struct Core *core);\nvoid disk_reset(struct Core *core);\n\nbool disk_prepare(struct Core *core);\nbool disk_saveFile(struct Core *core, int index, char *comment, int address, int length);\nbool disk_loadFile(struct Core *core, int index, int address, int maxLength, int offset, bool *pokeFailed);\n\n#endif /* disk_drive_h */\n"
  },
  {
    "path": "core/boot_intro.c",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"boot_intro.h\"\n#include \"core.h\"\n\nconst int bootIntroStateAddress = 0xA000;\n\nconst char *bootIntroSourceCode = \"VER$=\\\"\" CORE_VERSION \"\\\"\\n\\nGLOBAL JIN\\n\\nFONT 64\\n\\nENVELOPE 0,0,10,0,6\\nENVELOPE 1,0,10,0,6\\nENVELOPE 2,0,7,0,7\\nENVELOPE 3,0,7,0,7\\nLFO 0,5,4,0,0\\nLFO 1,5,4,0,0\\nVOLUME 0,15,%10\\nVOLUME 1,15,%01\\nVOLUME 2,15,%10\\nVOLUME 3,15,%01\\n\\nSPRITE.A 0,(3,0,0,0,1)\\nSPRITE.A 1,(0,0,0,0,1)\\nSPRITE.A 2,(0,0,0,0,3)\\nSPRITE.A 3,(1,0,0,0,0)\\nSPRITE.A 4,(2,0,0,0,1)\\n\\nSPRITE 1,72,56,5\\nSPRITE 2,64,56,1\\n\\nPAL 2\\nCELL 0,15,39\\nCELL 1,15,40\\nCELL 2,15,41\\nCELL 3,15,42\\nCELL 4,15,43\\nCELL 5,15,44\\n\\nTEXT 20-LEN(VER$),15,VER$\\n\\n\\nDO\\n SPRITE 3,76,40,7\\n WAIT 30\\n FOR Y=40 TO 56\\n  SPRITE 3,76,Y,7\\n  WAIT 2\\n NEXT Y\\n FOR I=1 TO 30\\n  WAIT VBL\\n  IF PEEK($A000)=1 THEN GOTO LOADING\\n NEXT I\\nLOOP\\n\\nLOADING:\\nON VBL CALL JINGLE\\nFOR Y=56 TO 49 STEP -1\\n SPRITE 0,72,Y,37\\n SPRITE 1,,Y,\\n WAIT 4\\nNEXT Y\\nWAIT 60\\n\\nPOKE $A000,2\\nDO\\n N=N+1\\n SPRITE 4,76,80,8+(N MOD 4)*2\\n WAIT 10\\nLOOP\\n\\nSUB JINGLE\\n IF JIN=0 THEN\\n  PLAY 0,52,0\\n  PLAY 1,48,0\\n ELSE IF JIN=15 THEN\\n  STOP\\n  PLAY 2,50,1\\n  PLAY 3,46,1\\n ELSE IF JIN=30 THEN\\n  ON VBL OFF\\n END IF\\n JIN=JIN+1\\nEND SUB\\n\\n#1:MAIN PALETTES\\n0A2A150030381500003F2F0F003F0A34\\n003F2A15003F2A15003F2A15003F2A15\\n\\n#2:MAIN CHARACTERS\\n00000000000000000000000000000000\\n3F7FC0809CBEBEBE3F40BFFFFFFFFFFF\\nFF80808080808080FFFFFFFFFFFFFFFF\\nFF01010101010101FFFFFFFFFFFFFFFF\\nFCFE0321210D6D61FC02FDDFFFFFFFFF\\n7FFF809F9F9F9F9F7F80FFFFFFFFFFFF\\nFEFF01F9F9F9F9F9FE01FFFFFFFFFFFF\\n7EBDBDBDBD8181FF7EC3DBC3C3FFFFFF\\n08090200C100204800412200C1002241\\n00000000800000000000000080000000\\n00412200C100224108482000C1000209\\n00000000800000000000000080000000\\n08482000C10002090849220000002249\\n00000000800000000000000000000000\\n084922000000224908090200C1002048\\n00000000000000000000000080000000\\n00000000000000000000000000000000\\n9C8088948880403FFFFFFFFFFFFF7F3F\\n80B6929B89B692FFFFC9FFE4FFC9FFFF\\n01D9496D25D949FFFF27FF93FF27FFFF\\n01011129110102FCFFFFFFFFFFFFFEFC\\n9F9F9F80FFFF80FFFFFFFFFFFF80FFFF\\nF9F9F901FFFF01FFFFFFFFFFFF01FFFF\\n00000000000000000000000000000000\\n08000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000800000000000000\\n00000000000000000000000000000000\\n08000000000000000800000000000000\\n00000000000000000000000000000000\\n08000000000000000800000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n0000000000000301000000000F0F0C0F\\n000000000000C08000000000F0F030F0\\n00004344444473000000434444447300\\n000022A2AAB62200000022A2AAB62200\\n0000E794E79497000000E794E7949700\\n00009C201804B80000009C201804B800\\n00004564544C450000004564544C4500\\n000010A040A01000000010A040A01000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000F0F000000000000\\n0000000000000000F0F0000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00000000000000000000000000000000\\n00181818180018000000000000000000\\n006C6C24000000000000000000000000\\n00247E24247E24000000000000000000\\n00083E380E3E08000000000000000000\\n00626408102646000000000000000000\\n001C34386E643A000000000000000000\\n00181830000000000000000000000000\\n00000408080804000000040808080400\\n00001008080810000000100808081000\\n000024187E1824000000000000000000\\n000018187E1818000000000000000000\\n00000000181830000000000000000000\\n000000007E0000000000000000000000\\n00000000000010000000000000001000\\n00060C18306040000000000000000000\\n00001C2222221C0000001C2222221C00\\n0000380808083E000000380808083E00\\n00003C021C203E0000003C021C203E00\\n00003C021C023C0000003C021C023C00\\n00002020283E080000002020283E0800\\n00003E203C023C0000003E203C023C00\\n00001C203C221C0000001C203C221C00\\n00003E040810200000003E0408102000\\n00001C221C221C0000001C221C221C00\\n00001C221E023C0000001C221E023C00\\n00000018001800000000000000000000\\n00000018001830000000000000000000\\n00000C1830180C000000000000000000\\n0000007E007E00000000000000000000\\n000030180C1830000000000000000000\\n003C660C180018000000000000000000\\n003C666E6E603C000000000000000000\\n00183C667E6666000000000000000000\\n007C667C66667C000000000000000000\\n003C666060663C000000000000000000\\n00786C66666C78000000000000000000\\n007E607860607E000000000000000000\\n007E6078606060000000000000000000\\n003C606E66663C000000000000000000\\n0066667E666666000000000000000000\\n003C181818183C000000000000000000\\n001E060606663C000000000000000000\\n00666C78786C66000000000000000000\\n0060606060607E000000000000000000\\n0042667E7E6666000000000000000000\\n0066767E6E6666000000000000000000\\n003C666666663C000000000000000000\\n007C667C606060000000000000000000\\n003C66666A6C3E000000000000000000\\n007C667C786C66000000000000000000\\n003E603C06067C000000000000000000\\n007E1818181818000000000000000000\\n0066666666663C000000000000000000\\n00666666663C18000000000000000000\\n0066667E7E6642000000000000000000\\n00663C183C6666000000000000000000\\n0066663C181818000000000000000000\\n007E0C1830607E000000000000000000\\n003C303030303C000000000000000000\\n006030180C0602000000000000000000\\n003C0C0C0C0C3C000000000000000000\\n00183C66000000000000000000000000\\n0000000000007E000000000000000000\\n\";\n"
  },
  {
    "path": "core/boot_intro.h",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef boot_intro_h\n#define boot_intro_h\n\nextern const int bootIntroStateAddress;\nextern const char *bootIntroSourceCode;\n\nenum BootIntroState {\n    BootIntroStateDefault,\n    BootIntroStateProgramAvailable,\n    BootIntroStateReadyToRun,\n    BootIntroStateDone\n};\n\n#endif /* boot_intro_h */\n"
  },
  {
    "path": "core/core.c",
    "content": "//\n// Copyright 2016-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"core.h\"\n#include <stdlib.h>\n#include <math.h>\n#include <string.h>\n#include \"string_utils.h\"\n#include \"startup_sequence.h\"\n\nconst char CoreInputKeyReturn = '\\n';\nconst char CoreInputKeyBackspace = '\\b';\nconst char CoreInputKeyRight = 17;\nconst char CoreInputKeyLeft = 18;\nconst char CoreInputKeyDown = 19;\nconst char CoreInputKeyUp = 20;\n\nvoid core_handleInput(struct Core *core, struct CoreInput *input);\n\n\nvoid core_init(struct Core *core)\n{\n    memset(core, 0, sizeof(struct Core));\n    \n    core->machine = calloc(1, sizeof(struct Machine));\n    if (!core->machine) exit(EXIT_FAILURE);\n    \n    core->machineInternals = calloc(1, sizeof(struct MachineInternals));\n    if (!core->machineInternals) exit(EXIT_FAILURE);\n    \n    core->interpreter = calloc(1, sizeof(struct Interpreter));\n    if (!core->interpreter) exit(EXIT_FAILURE);\n    \n    core->diskDrive = calloc(1, sizeof(struct DiskDrive));\n    if (!core->diskDrive) exit(EXIT_FAILURE);\n    \n    core->overlay = calloc(1, sizeof(struct Overlay));\n    if (!core->overlay) exit(EXIT_FAILURE);\n    \n    machine_init(core);\n    itp_init(core);\n    overlay_init(core);\n    disk_init(core);\n}\n\nvoid core_deinit(struct Core *core)\n{\n    itp_deinit(core);\n    disk_deinit(core);\n    \n    free(core->machine);\n    core->machine = NULL;\n    \n    free(core->machineInternals);\n    core->machineInternals = NULL;\n    \n    free(core->interpreter);\n    core->interpreter = NULL;\n    \n    free(core->diskDrive);\n    core->diskDrive = NULL;\n    \n    free(core->overlay);\n    core->overlay = NULL;\n}\n\nvoid core_setDelegate(struct Core *core, struct CoreDelegate *delegate)\n{\n    core->delegate = delegate;\n}\n\nstruct CoreError core_compileProgram(struct Core *core, const char *sourceCode, bool resetPersistent)\n{\n    machine_reset(core, resetPersistent);\n    overlay_reset(core);\n    disk_reset(core);\n    return itp_compileProgram(core, sourceCode);\n}\n\nvoid core_traceError(struct Core *core, struct CoreError error)\n{\n    core->interpreter->debug = false;\n    struct TextLib *lib = &core->overlay->textLib;\n    txtlib_printText(lib, err_getString(error.code));\n    txtlib_printText(lib, \"\\n\");\n    if (error.sourcePosition >= 0 && core->interpreter->sourceCode)\n    {\n        int number = lineNumber(core->interpreter->sourceCode, error.sourcePosition);\n        char lineNumberText[30];\n        sprintf(lineNumberText, \"IN LINE %d:\\n\", number);\n        txtlib_printText(lib, lineNumberText);\n        \n        const char *line = lineString(core->interpreter->sourceCode, error.sourcePosition);\n        if (line)\n        {\n            txtlib_printText(lib, line);\n            txtlib_printText(lib, \"\\n\");\n            free((void *)line);\n        }\n    }\n}\n\nvoid core_willRunProgram(struct Core *core, long secondsSincePowerOn)\n{\n    runStartupSequence(core);\n    core->interpreter->timer = (float)(secondsSincePowerOn * 60 % TIMER_WRAP_VALUE);\n    machine_suspendEnergySaving(core, 30);\n    delegate_controlsDidChange(core);\n}\n\nvoid core_update(struct Core *core, struct CoreInput *input)\n{\n    core_handleInput(core, input);\n    itp_runInterrupt(core, InterruptTypeVBL);\n    itp_runProgram(core);\n    itp_didFinishVBL(core);\n    overlay_draw(core, true);\n    audio_bufferRegisters(core);\n}\n\nvoid core_handleInput(struct Core *core, struct CoreInput *input)\n{\n    struct IORegisters *ioRegisters = &core->machine->ioRegisters;\n    union IOAttributes ioAttr = ioRegisters->attr;\n    \n    bool processedOtherInput = false;\n    \n    if (input->key != 0)\n    {\n        if (ioAttr.keyboardEnabled)\n        {\n            char key = input->key;\n            if (   (key >= 32 && key < 127)\n                || key == CoreInputKeyBackspace || key == CoreInputKeyReturn\n                || key == CoreInputKeyDown || key == CoreInputKeyUp || key == CoreInputKeyRight || key == CoreInputKeyLeft )\n            {\n                ioRegisters->key = key;\n            }\n        }\n        input->key = 0;\n        machine_suspendEnergySaving(core, 2);\n    }\n    \n    if (input->touch)\n    {\n        if (ioAttr.touchEnabled)\n        {\n            ioRegisters->status.touch = 1;\n            int x = input->touchX;\n            int y = input->touchY;\n            if (x < 0) x = 0; else if (x >= SCREEN_WIDTH) x = SCREEN_WIDTH - 1;\n            if (y < 0) y = 0; else if (y >= SCREEN_HEIGHT) y = SCREEN_HEIGHT - 1;\n            ioRegisters->touchX = x;\n            ioRegisters->touchY = y;\n        }\n        else\n        {\n            ioRegisters->status.touch = 0;\n        }\n        machine_suspendEnergySaving(core, 2);\n    }\n    else\n    {\n        ioRegisters->status.touch = 0;\n    }\n    \n    for (int i = 0; i < NUM_GAMEPADS; i++)\n    {\n        union Gamepad *gamepad = &ioRegisters->gamepads[i];\n        if (ioAttr.gamepadsEnabled > i && !ioAttr.keyboardEnabled)\n        {\n            struct CoreInputGamepad *inputGamepad = &input->gamepads[i];\n            gamepad->up = inputGamepad->up && !inputGamepad->down;\n            gamepad->down = inputGamepad->down && !inputGamepad->up;\n            gamepad->left = inputGamepad->left && !inputGamepad->right;\n            gamepad->right = inputGamepad->right && !inputGamepad->left;\n            gamepad->buttonA = inputGamepad->buttonA;\n            gamepad->buttonB = inputGamepad->buttonB;\n            \n            if (inputGamepad->up || inputGamepad->down || inputGamepad->left || inputGamepad->right)\n            {\n                // some d-pad combinations are not registered as I/O, but mark them anyway.\n                processedOtherInput = true;\n            }\n            \n            if (gamepad->value)\n            {\n                machine_suspendEnergySaving(core, 2);\n            }\n        }\n        else\n        {\n            gamepad->value = 0;\n        }\n    }\n    \n    if (input->pause)\n    {\n        if (core->interpreter->state == StatePaused)\n        {\n            core->interpreter->state = StateEvaluate;\n            overlay_updateState(core);\n            processedOtherInput = true;\n        }\n        else if (ioAttr.gamepadsEnabled > 0 && !ioAttr.keyboardEnabled) {\n            ioRegisters->status.pause = 1;\n        }\n        input->pause = false;\n    }\n    \n    input->out_hasUsedInput = processedOtherInput || ioRegisters->key || ioRegisters->status.value || ioRegisters->gamepads[0].value || ioRegisters->gamepads[1].value;\n}\n\nvoid core_willSuspendProgram(struct Core *core)\n{\n    if (core->machineInternals->hasChangedPersistent)\n    {\n        delegate_persistentRamDidChange(core, core->machine->persistentRam, PERSISTENT_RAM_SIZE);\n        core->machineInternals->hasChangedPersistent = false;\n    }\n}\n\nvoid core_setDebug(struct Core *core, bool enabled)\n{\n    core->interpreter->debug = enabled;\n    overlay_updateState(core);\n}\n\nbool core_getDebug(struct Core *core)\n{\n    return core->interpreter->debug;\n}\n\nbool core_isKeyboardEnabled(struct Core *core)\n{\n    return core->machine->ioRegisters.attr.keyboardEnabled;\n}\n\nbool core_shouldRender(struct Core *core)\n{\n    enum State state = core->interpreter->state;\n    bool shouldRender = (!core->machineInternals->isEnergySaving && state != StateEnd && state != StateNoProgram)\n    || core->machineInternals->energySavingTimer > 0\n    || core->machineInternals->energySavingTimer % 20 == 0;\n    \n    core->machineInternals->energySavingTimer--;\n    return shouldRender;\n}\n\nvoid core_setInputGamepad(struct CoreInput *input, int player, bool up, bool down, bool left, bool right, bool buttonA, bool buttonB)\n{\n    struct CoreInputGamepad *gamepad = &input->gamepads[player];\n    gamepad->up = up;\n    gamepad->down = down;\n    gamepad->left = left;\n    gamepad->right = right;\n    gamepad->buttonA = buttonA;\n    gamepad->buttonB = buttonB;\n}\n\nvoid core_diskLoaded(struct Core *core)\n{\n    core->interpreter->state = StateEvaluate;\n}\n"
  },
  {
    "path": "core/core.h",
    "content": "//\n// Copyright 2016-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef core_h\n#define core_h\n\n#define CORE_VERSION \"1.2\"\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"machine.h\"\n#include \"overlay.h\"\n#include \"interpreter.h\"\n#include \"disk_drive.h\"\n#include \"core_delegate.h\"\n\nstruct Core {\n    struct Machine *machine;\n    struct MachineInternals *machineInternals;\n    struct Interpreter *interpreter;\n    struct DiskDrive *diskDrive;\n    struct Overlay *overlay;\n    struct CoreDelegate *delegate;\n};\n\nstruct CoreInputGamepad {\n    bool up;\n    bool down;\n    bool left;\n    bool right;\n    bool buttonA;\n    bool buttonB;\n};\n\nstruct CoreInput {\n    struct CoreInputGamepad gamepads[NUM_GAMEPADS];\n    bool pause;\n    int touchX;\n    int touchY;\n    bool touch;\n    char key;\n    bool out_hasUsedInput;\n};\n\nextern const char CoreInputKeyReturn;\nextern const char CoreInputKeyBackspace;\nextern const char CoreInputKeyRight;\nextern const char CoreInputKeyLeft;\nextern const char CoreInputKeyDown;\nextern const char CoreInputKeyUp;\n\nvoid core_init(struct Core *core);\nvoid core_deinit(struct Core *core);\nvoid core_setDelegate(struct Core *core, struct CoreDelegate *delegate);\nstruct CoreError core_compileProgram(struct Core *core, const char *sourceCode, bool resetPersistent);\nvoid core_traceError(struct Core *core, struct CoreError error);\nvoid core_willRunProgram(struct Core *core, long secondsSincePowerOn);\nvoid core_update(struct Core *core, struct CoreInput *input);\nvoid core_willSuspendProgram(struct Core *core);\nvoid core_setDebug(struct Core *core, bool enabled);\nbool core_getDebug(struct Core *core);\nbool core_isKeyboardEnabled(struct Core *core);\nbool core_shouldRender(struct Core *core);\n\nvoid core_setInputGamepad(struct CoreInput *input, int player, bool up, bool down, bool left, bool right, bool buttonA, bool buttonB);\n\nvoid core_diskLoaded(struct Core *core);\n\n// for dev mode only:\nvoid core_handleInput(struct Core *core, struct CoreInput *input);\n\n#endif /* core_h */\n"
  },
  {
    "path": "core/core_delegate.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include <stdio.h>\n#include \"core.h\"\n\nvoid delegate_interpreterDidFail(struct Core *core, struct CoreError coreError)\n{\n    if (core->delegate->interpreterDidFail)\n    {\n        core->delegate->interpreterDidFail(core->delegate->context, coreError);\n    }\n}\n\nbool delegate_diskDriveWillAccess(struct Core *core)\n{\n    if (core->delegate->diskDriveWillAccess)\n    {\n        return core->delegate->diskDriveWillAccess(core->delegate->context, &core->diskDrive->dataManager);\n    }\n    return true;\n}\n\nvoid delegate_diskDriveDidSave(struct Core *core)\n{\n    if (core->delegate->diskDriveDidSave)\n    {\n        core->delegate->diskDriveDidSave(core->delegate->context, &core->diskDrive->dataManager);\n    }\n}\n\nvoid delegate_diskDriveIsFull(struct Core *core)\n{\n    if (core->delegate->diskDriveIsFull)\n    {\n        core->delegate->diskDriveIsFull(core->delegate->context, &core->diskDrive->dataManager);\n    }\n}\n\nvoid delegate_controlsDidChange(struct Core *core)\n{\n    if (core->delegate->controlsDidChange)\n    {\n        struct ControlsInfo info;\n        union IOAttributes ioAttr = core->machine->ioRegisters.attr;\n        if (ioAttr.keyboardEnabled)\n        {\n            if (core->interpreter->isKeyboardOptional)\n            {\n                info.keyboardMode = KeyboardModeOptional;\n            }\n            else\n            {\n                info.keyboardMode = KeyboardModeOn;\n            }\n        }\n        else\n        {\n            info.keyboardMode = KeyboardModeOff;\n        }\n        info.numGamepadsEnabled = ioAttr.keyboardEnabled ? 0 : ioAttr.gamepadsEnabled;\n        info.isTouchEnabled = ioAttr.touchEnabled;\n        info.isAudioEnabled = core->machineInternals->audioInternals.audioEnabled;\n        core->delegate->controlsDidChange(core->delegate->context, info);\n    }\n}\n\nvoid delegate_persistentRamWillAccess(struct Core *core, uint8_t *destination, int size)\n{\n    if (core->delegate->persistentRamWillAccess)\n    {\n        core->delegate->persistentRamWillAccess(core->delegate->context, destination, size);\n    }\n}\n\nvoid delegate_persistentRamDidChange(struct Core *core, uint8_t *data, int size)\n{\n    if (core->delegate->persistentRamDidChange)\n    {\n        core->delegate->persistentRamDidChange(core->delegate->context, data, size);\n    }\n}\n"
  },
  {
    "path": "core/core_delegate.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef core_delegate_h\n#define core_delegate_h\n\n#include \"data_manager.h\"\n#include \"error.h\"\n\nstruct Core;\n\nenum KeyboardMode {\n    KeyboardModeOff,\n    KeyboardModeOn,\n    KeyboardModeOptional\n};\n\nstruct ControlsInfo {\n    enum KeyboardMode keyboardMode;\n    int numGamepadsEnabled;\n    bool isTouchEnabled;\n    bool isAudioEnabled;\n};\n\nstruct CoreDelegate {\n    void *context;\n    \n    /** Called on error */\n    void (*interpreterDidFail)(void *context, struct CoreError coreError);\n    \n    /** Returns true if the disk is ready, false if not. In case of not, core_diskLoaded must be called when ready. */\n    bool (*diskDriveWillAccess)(void *context, struct DataManager *diskDataManager);\n    \n    /** Called when a disk data entry was saved */\n    void (*diskDriveDidSave)(void *context, struct DataManager *diskDataManager);\n    \n    /** Called when a disk data entry was tried to be saved, but the disk is full */\n    void (*diskDriveIsFull)(void *context, struct DataManager *diskDataManager);\n    \n    /** Called when keyboard or gamepad settings changed */\n    void (*controlsDidChange)(void *context, struct ControlsInfo controlsInfo);\n    \n    /** Called when persistent RAM will be accessed the first time */\n    void (*persistentRamWillAccess)(void *context, uint8_t *destination, int size);\n    \n    /** Called when persistent RAM should be saved */\n    void (*persistentRamDidChange)(void *context, uint8_t *data, int size);\n};\n\nvoid delegate_interpreterDidFail(struct Core *core, struct CoreError coreError);\nbool delegate_diskDriveWillAccess(struct Core *core);\nvoid delegate_diskDriveDidSave(struct Core *core);\nvoid delegate_diskDriveIsFull(struct Core *core);\nvoid delegate_controlsDidChange(struct Core *core);\nvoid delegate_persistentRamWillAccess(struct Core *core, uint8_t *destination, int size);\nvoid delegate_persistentRamDidChange(struct Core *core, uint8_t *data, int size);\n\n#endif /* core_delegate_h */\n"
  },
  {
    "path": "core/core_stats.c",
    "content": "//\n// Copyright 2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"core_stats.h\"\n#include <string.h>\n#include <stdlib.h>\n#include \"string_utils.h\"\n\nvoid stats_init(struct Stats *stats)\n{\n    memset(stats, 0, sizeof(struct Stats));\n    \n    stats->tokenizer = calloc(1, sizeof(struct Tokenizer));\n    if (!stats->tokenizer) exit(EXIT_FAILURE);\n    \n    stats->romDataManager = calloc(1, sizeof(struct DataManager));\n    if (!stats->romDataManager) exit(EXIT_FAILURE);\n    \n    stats->romDataManager->data = calloc(1, DATA_SIZE);\n    if (!stats->romDataManager->data) exit(EXIT_FAILURE);\n}\n\nvoid stats_deinit(struct Stats *stats)\n{\n    free(stats->romDataManager->data);\n    stats->romDataManager->data = NULL;\n    \n    free(stats->tokenizer);\n    stats->tokenizer = NULL;\n    \n    free(stats->romDataManager);\n    stats->romDataManager = NULL;\n}\n\nstruct CoreError stats_update(struct Stats *stats, const char *sourceCode)\n{\n    stats->numTokens = 0;\n    stats->romSize = 0;\n    \n    struct CoreError error = err_noCoreError();\n    \n    const char *upperCaseSourceCode = uppercaseString(sourceCode);\n    if (!upperCaseSourceCode)\n    {\n        error = err_makeCoreError(ErrorOutOfMemory, -1);\n        goto cleanup;\n    }\n    \n    error = tok_tokenizeUppercaseProgram(stats->tokenizer, upperCaseSourceCode);\n    if (error.code != ErrorNone)\n    {\n        goto cleanup;\n    }\n    \n    stats->numTokens = stats->tokenizer->numTokens;\n    \n    struct DataManager *romDataManager = stats->romDataManager;\n    error = data_uppercaseImport(romDataManager, upperCaseSourceCode, false);\n    if (error.code != ErrorNone)\n    {\n        goto cleanup;\n    }\n    \n    stats->romSize = data_currentSize(stats->romDataManager);\n    \n    // add default characters if ROM entry 0 is unused\n    struct DataEntry *entry0 = &romDataManager->entries[0];\n    if (entry0->length == 0 && (DATA_SIZE - data_currentSize(romDataManager)) >= 1024)\n    {\n        stats->romSize += 1024;\n    }\n    \ncleanup:\n    tok_freeTokens(stats->tokenizer);\n    if (upperCaseSourceCode)\n    {\n        free((void *)upperCaseSourceCode);\n    }\n    \n    return error;\n}\n"
  },
  {
    "path": "core/core_stats.h",
    "content": "//\n// Copyright 2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef core_stats_h\n#define core_stats_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"tokenizer.h\"\n#include \"data_manager.h\"\n\nstruct Stats {\n    struct Tokenizer *tokenizer;\n    struct DataManager *romDataManager;\n    int numTokens;\n    int romSize;\n};\n\nvoid stats_init(struct Stats *stats);\nvoid stats_deinit(struct Stats *stats);\nstruct CoreError stats_update(struct Stats *stats, const char *sourceCode);\n\n#endif /* core_stats_h */\n"
  },
  {
    "path": "core/datamanager/data_manager.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"data_manager.h\"\n#include \"charsets.h\"\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n#include \"string_utils.h\"\n\nint data_calcOutputSize(struct DataManager *manager);\n\nvoid data_init(struct DataManager *manager)\n{\n    data_reset(manager);\n}\n\nvoid data_deinit(struct DataManager *manager)\n{\n    assert(manager);\n    \n    if (manager->diskSourceCode)\n    {\n        free((void *)manager->diskSourceCode);\n        manager->diskSourceCode = NULL;\n    }\n}\n\nvoid data_reset(struct DataManager *manager)\n{\n    memset(manager->entries, 0, sizeof(struct DataEntry) * MAX_ENTRIES);\n    \n    strcpy(manager->entries[1].comment, \"MAIN PALETTES\");\n    strcpy(manager->entries[2].comment, \"MAIN CHARACTERS\");\n    strcpy(manager->entries[3].comment, \"MAIN BG\");\n    strcpy(manager->entries[15].comment, \"MAIN SOUND\");\n\n    if (manager->diskSourceCode)\n    {\n        free((void *)manager->diskSourceCode);\n        manager->diskSourceCode = NULL;\n    }\n}\n\nstruct CoreError data_import(struct DataManager *manager, const char *input, bool keepSourceCode)\n{\n    assert(manager);\n    assert(input);\n    \n    const char *uppercaseInput = uppercaseString(input);\n    if (!uppercaseInput) return err_makeCoreError(ErrorOutOfMemory, -1);\n    \n    struct CoreError error = data_uppercaseImport(manager, uppercaseInput, keepSourceCode);\n    free((void *)uppercaseInput);\n    \n    return error;\n}\n\nstruct CoreError data_uppercaseImport(struct DataManager *manager, const char *input, bool keepSourceCode)\n{\n    assert(manager);\n    assert(input);\n    \n    data_reset(manager);\n    \n    const char *character = input;\n    uint8_t *currentDataByte = manager->data;\n    uint8_t *endDataByte = &manager->data[DATA_SIZE];\n    \n    // skip stuff before\n    const char *prevChar = NULL;\n    while (*character && !(*character == '#' && (!prevChar || *prevChar == '\\n')))\n    {\n        prevChar = character;\n        character++;\n    }\n    \n    if (keepSourceCode)\n    {\n        size_t length = (size_t)(character - input);\n        \n        char *diskSourceCode = malloc(length + 1);\n        if (!diskSourceCode) exit(EXIT_FAILURE);\n        \n        stringConvertCopy(diskSourceCode, input, length);\n        manager->diskSourceCode = diskSourceCode;\n    }\n    \n    while (*character)\n    {\n        if (*character == '#')\n        {\n            character++;\n            \n            // entry index\n            int entryIndex = 0;\n            while (*character)\n            {\n                if (strchr(CharSetDigits, *character))\n                {\n                    int digit = (int)*character - (int)'0';\n                    entryIndex *= 10;\n                    entryIndex += digit;\n                    character++;\n                }\n                else\n                {\n                    break;\n                }\n            }\n            if (*character != ':') return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - input));\n            character++;\n            \n            if (entryIndex >= MAX_ENTRIES) return err_makeCoreError(ErrorIndexOutOfBounds, (int)(character - input));\n            \n            struct DataEntry *entry = &manager->entries[entryIndex];\n            if (entry->length > 0) return err_makeCoreError(ErrorIndexAlreadyDefined, (int)(character - input));\n            \n            // file comment\n            const char *comment = character;\n            do\n            {\n                character++;\n            }\n            while (*character && *character != '\\n' && *character != '\\r');\n            size_t commentLen = (character - comment);\n            if (commentLen >= ENTRY_COMMENT_SIZE) commentLen = ENTRY_COMMENT_SIZE - 1;\n            memset(entry->comment, 0, ENTRY_COMMENT_SIZE);\n            strncpy(entry->comment, comment, commentLen);\n            \n            // binary data\n            uint8_t *startByte = currentDataByte;\n            bool shift = true;\n            int value = 0;\n            while (*character && *character != '#')\n            {\n                char *spos = strchr(CharSetHex, *character);\n                if (spos)\n                {\n                    int digit = (int)(spos - CharSetHex);\n                    if (shift)\n                    {\n                        value = digit << 4;\n                    }\n                    else\n                    {\n                        value |= digit;\n                        if (currentDataByte >= endDataByte) return err_makeCoreError(ErrorRomIsFull, (int)(character - input));\n                        *currentDataByte = value;\n                        ++currentDataByte;\n                    }\n                    shift = !shift;\n                }\n                else if (*character != ' ' && *character != '\\t' && *character != '\\n' && *character != '\\r')\n                {\n                    return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - input));\n                }\n                character++;\n            }\n            if (!shift) return err_makeCoreError(ErrorSyntax, (int)(character - input)); // incomplete hex value\n            \n            int start = (int)(startByte - manager->data);\n            int length = (int)(currentDataByte - startByte);\n            entry->start = start;\n            entry->length = length;\n            \n            for (int i = entryIndex + 1; i < MAX_ENTRIES; i++)\n            {\n                manager->entries[i].start = entry->start + entry->length;\n            }\n        }\n        else if (*character == ' ' || *character == '\\t' || *character == '\\n' || *character == '\\r')\n        {\n            character++;\n        }\n        else\n        {\n            return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - input));\n        }\n    }\n    return err_noCoreError();\n}\n\nchar *data_export(struct DataManager *manager)\n{\n    assert(manager);\n    \n    size_t outputSize = data_calcOutputSize(manager);\n    if (outputSize > 0)\n    {\n        char *output = malloc(outputSize);\n        if (output)\n        {\n            char *current = output;\n            \n            if (manager->diskSourceCode)\n            {\n                size_t len = strlen(manager->diskSourceCode);\n                if (len > 0)\n                {\n                    strcpy(current, manager->diskSourceCode);\n                    char endChar = current[len - 1];\n                    current += len;\n                    if (endChar != '\\n')\n                    {\n                        // add new line after end of program\n                        current[0] = '\\n';\n                        current++;\n                    }\n                }\n            }\n            \n            for (int i = 0; i < MAX_ENTRIES; i++)\n            {\n                struct DataEntry *entry = &manager->entries[i];\n                if (entry->length > 0)\n                {\n                    sprintf(current, \"#%d:%s\\n\", i, entry->comment);\n                    current += strlen(current);\n                    int valuesInLine = 0;\n                    int pos = 0;\n                    uint8_t *entryData = &manager->data[entry->start];\n                    while (pos < entry->length)\n                    {\n                        sprintf(current, \"%02X\", entryData[pos]);\n                        current += strlen(current);\n                        pos++;\n                        valuesInLine++;\n                        if (pos == entry->length)\n                        {\n                            sprintf(current, \"\\n\\n\");\n                        }\n                        else if (valuesInLine == 16)\n                        {\n                            sprintf(current, \"\\n\");\n                            valuesInLine = 0;\n                        }\n                        current += strlen(current);\n                    }\n                    \n                }\n            }\n        }\n        return output;\n    }\n    return NULL;\n}\n\nint data_calcOutputSize(struct DataManager *manager)\n{\n    int size = 0;\n    for (int i = 0; i < MAX_ENTRIES; i++)\n    {\n        struct DataEntry *entry = &manager->entries[i];\n        if (entry->length > 0)\n        {\n            size += (i >= 10 ? 4 : 3) + strlen(entry->comment) + 1; // #10:comment\\n\n            size += entry->length * 2; // 2x hex letters\n            size += entry->length / 16 + 1; // new line every 16 values\n            size += 1; // new line\n        }\n    }\n    if (manager->diskSourceCode)\n    {\n        size += strlen(manager->diskSourceCode) + 1; // possible new line between program and data\n    }\n    size += 1; // 0-byte\n    return size;\n}\n\nint data_currentSize(struct DataManager *manager)\n{\n    int size = 0;\n    for (int i = 0; i < MAX_ENTRIES; i++)\n    {\n        size += manager->entries[i].length;\n    }\n    return size;\n}\n\nbool data_canSetEntry(struct DataManager *manager, int index, int length)\n{\n    int size = 0;\n    for (int i = 0; i < MAX_ENTRIES; i++)\n    {\n        if (i != index)\n        {\n            size += manager->entries[i].length;\n        }\n    }\n    return size + length <= DATA_SIZE;\n}\n\nvoid data_setEntry(struct DataManager *manager, int index, const char *comment, uint8_t *source, int length)\n{\n    struct DataEntry *entry = &manager->entries[index];\n    uint8_t *data = manager->data;\n        \n    // move data of higher entries\n    int nextStart = entry->start + length;\n    assert(nextStart <= DATA_SIZE);\n        \n    if (length > entry->length) // new entry is bigger\n    {\n        int diff = length - entry->length;\n        for (int i = DATA_SIZE - 1; i >= nextStart; i--)\n        {\n            data[i] = data[i - diff];\n        }\n    }\n    else if (length < entry->length) // new entry is smaller\n    {\n        int diff = entry->length - length;\n        for (int i = nextStart; i < DATA_SIZE - diff; i++)\n        {\n            data[i] = data[i + diff];\n        }\n        for (int i = DATA_SIZE - diff; i < DATA_SIZE; i++)\n        {\n            data[i] = 0;\n        }\n    }\n    \n    // write new entry\n    strncpy(entry->comment, comment, ENTRY_COMMENT_SIZE);\n    entry->comment[ENTRY_COMMENT_SIZE - 1] = 0;\n    entry->length = length;\n    int start = entry->start;\n    for (int i = 0; i < length; i++)\n    {\n        data[i + start] = source[i];\n    }\n    \n    // move entry positions\n    for (int i = index + 1; i < MAX_ENTRIES; i++)\n    {\n        struct DataEntry *thisEntry = &manager->entries[i];\n        struct DataEntry *prevEntry = &manager->entries[i - 1];\n        thisEntry->start = prevEntry->start + prevEntry->length;\n    }\n}\n"
  },
  {
    "path": "core/datamanager/data_manager.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef data_manager_h\n#define data_manager_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include \"error.h\"\n\n#define MAX_ENTRIES 16\n#define DATA_SIZE 0x8000\n#define ENTRY_COMMENT_SIZE 32\n\nstruct DataEntry {\n    char comment[ENTRY_COMMENT_SIZE];\n    int start;\n    int length;\n};\n\nstruct DataManager {\n    struct DataEntry entries[MAX_ENTRIES];\n    uint8_t *data;\n    const char *diskSourceCode;\n};\n\nvoid data_init(struct DataManager *manager);\nvoid data_deinit(struct DataManager *manager);\nvoid data_reset(struct DataManager *manager);\nstruct CoreError data_import(struct DataManager *manager, const char *input, bool keepSourceCode);\nstruct CoreError data_uppercaseImport(struct DataManager *manager, const char *input, bool keepSourceCode);\nchar *data_export(struct DataManager *manager);\n\nint data_currentSize(struct DataManager *manager);\n\nbool data_canSetEntry(struct DataManager *manager, int index, int length);\nvoid data_setEntry(struct DataManager *manager, int index, const char *comment, uint8_t *source, int length);\n\n#endif /* data_manager_h */\n"
  },
  {
    "path": "core/interpreter/charsets.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"charsets.h\"\n\nconst char *CharSetDigits = \"0123456789\";\nconst char *CharSetLetters = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ_\";\nconst char *CharSetAlphaNum = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789\";\nconst char *CharSetHex = \"0123456789ABCDEF\";\n"
  },
  {
    "path": "core/interpreter/charsets.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef charsets_h\n#define charsets_h\n\nextern const char *CharSetDigits;\nextern const char *CharSetLetters;\nextern const char *CharSetAlphaNum;\nextern const char *CharSetHex;\n\n#endif /* charsets_h */\n"
  },
  {
    "path": "core/interpreter/cmd_audio.c",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_audio.h\"\n#include \"core.h\"\n#include \"interpreter_utils.h\"\n\nenum ErrorCode cmd_SOUND(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SOUND\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // wave value\n    struct TypedValue waveValue = itp_evaluateOptionalNumericExpression(core, 0, 3);\n    if (waveValue.type == ValueTypeError) return waveValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // pulse width value\n    struct TypedValue pwValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (pwValue.type == ValueTypeError) return pwValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // length value\n    struct TypedValue lenValue = itp_evaluateOptionalNumericExpression(core, 0, 255);\n    if (lenValue.type == ValueTypeError) return lenValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        struct Voice *voice = &core->machine->audioRegisters.voices[n];\n        if (waveValue.type != ValueTypeNull)\n        {\n            voice->attr.wave = waveValue.v.floatValue;\n        }\n        if (pwValue.type != ValueTypeNull)\n        {\n            voice->attr.pulseWidth = pwValue.v.floatValue;\n        }\n        if (lenValue.type != ValueTypeNull)\n        {\n            int len = lenValue.v.floatValue;\n            voice->length = len;\n            voice->attr.timeout = (len > 0) ? 1 : 0;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\n//enum ErrorCode cmd_SOUND_COPY(struct Core *core)\n//{\n//    struct Interpreter *interpreter = core->interpreter;\n//\n//    // SOUND COPY\n//    ++interpreter->pc;\n//    ++interpreter->pc;\n//    \n//    // sound value\n//    struct TypedValue sValue = itp_evaluateNumericExpression(core, 0, 15);\n//    if (sValue.type == ValueTypeError) return sValue.v.errorCode;\n//    \n//    // TO\n//    if (interpreter->pc->type != TokenTO) return ErrorSyntax;\n//    ++interpreter->pc;\n//    \n//    // voice value\n//    struct TypedValue vValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n//    if (vValue.type == ValueTypeError) return vValue.v.errorCode;\n//\n//    if (interpreter->pass == PassRun)\n//    {\n//        audlib_copySound(&interpreter->audioLib, interpreter->audioLib.sourceAddress, sValue.v.floatValue, vValue.v.floatValue);\n//    }\n//    \n//    return itp_endOfCommand(interpreter);\n//}\n\nenum ErrorCode cmd_VOLUME(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // VOLUME\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // volume value\n    struct TypedValue volValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (volValue.type == ValueTypeError) return volValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // mix value\n    struct TypedValue mixValue = itp_evaluateOptionalNumericExpression(core, 0, 3);\n    if (mixValue.type == ValueTypeError) return mixValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        struct Voice *voice = &core->machine->audioRegisters.voices[n];\n        if (volValue.type != ValueTypeNull)\n        {\n            voice->status.volume = volValue.v.floatValue;\n        }\n        if (mixValue.type != ValueTypeNull)\n        {\n            int mix = mixValue.v.floatValue;\n            voice->status.mix = mix;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_ENVELOPE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // ENVELOPE\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // attack value\n    struct TypedValue attValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (attValue.type == ValueTypeError) return attValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // decay value\n    struct TypedValue decValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (decValue.type == ValueTypeError) return decValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // sustain value\n    struct TypedValue susValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (susValue.type == ValueTypeError) return susValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // release value\n    struct TypedValue relValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (relValue.type == ValueTypeError) return relValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        struct Voice *voice = &core->machine->audioRegisters.voices[n];\n        if (attValue.type != ValueTypeNull)\n        {\n            voice->envA = attValue.v.floatValue;\n        }\n        if (decValue.type != ValueTypeNull)\n        {\n            voice->envD = decValue.v.floatValue;\n        }\n        if (susValue.type != ValueTypeNull)\n        {\n            voice->envS = susValue.v.floatValue;\n        }\n        if (relValue.type != ValueTypeNull)\n        {\n            voice->envR = relValue.v.floatValue;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_LFO(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // LFO\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // rate value\n    struct TypedValue rateValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (rateValue.type == ValueTypeError) return rateValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // osc amount value\n    struct TypedValue oscValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (oscValue.type == ValueTypeError) return oscValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // vol amount value\n    struct TypedValue volValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (volValue.type == ValueTypeError) return volValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // pw amount value\n    struct TypedValue pwValue = itp_evaluateOptionalNumericExpression(core, 0, 15);\n    if (pwValue.type == ValueTypeError) return pwValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        struct Voice *voice = &core->machine->audioRegisters.voices[n];\n        if (rateValue.type != ValueTypeNull)\n        {\n            voice->lfoFrequency = rateValue.v.floatValue;\n        }\n        if (oscValue.type != ValueTypeNull)\n        {\n            voice->lfoOscAmount = oscValue.v.floatValue;\n        }\n        if (volValue.type != ValueTypeNull)\n        {\n            voice->lfoVolAmount = volValue.v.floatValue;\n        }\n        if (pwValue.type != ValueTypeNull)\n        {\n            voice->lfoPWAmount = pwValue.v.floatValue;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_LFO_A(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // LFO.A\n    ++interpreter->pc;\n    \n    // obsolete syntax!\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    struct Voice *voice = NULL;\n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        voice = &core->machine->audioRegisters.voices[n];\n    }\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    union LFOAttributes attr;\n    if (voice) attr = voice->lfoAttr; else attr.value = 0;\n    \n    // attr value\n    struct TypedValue attrValue = itp_evaluateLFOAttributes(core, attr);\n    if (attrValue.type == ValueTypeError) return attrValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        voice->lfoAttr.value = attrValue.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_LFO_WAVE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // LFO WAVE\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // wave value\n    struct TypedValue wavValue = itp_evaluateOptionalNumericExpression(core, 0, 3);\n    if (wavValue.type == ValueTypeError) return wavValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // inv value\n    struct TypedValue invValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n    if (invValue.type == ValueTypeError) return invValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // env value\n    struct TypedValue envValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n    if (envValue.type == ValueTypeError) return envValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // tri value\n    struct TypedValue triValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n    if (triValue.type == ValueTypeError) return triValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        struct Voice *voice = &core->machine->audioRegisters.voices[n];\n        \n        if (wavValue.type != ValueTypeNull) voice->lfoAttr.wave = wavValue.v.floatValue;\n        if (invValue.type != ValueTypeNull) voice->lfoAttr.invert = invValue.v.floatValue ? 1 : 0;\n        if (envValue.type != ValueTypeNull) voice->lfoAttr.envMode = envValue.v.floatValue ? 1 : 0;\n        if (triValue.type != ValueTypeNull) voice->lfoAttr.trigger = triValue.v.floatValue ? 1 : 0;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_PLAY(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // PLAY\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // pitch value\n    struct TypedValue pValue = itp_evaluateNumericExpression(core, 0, 96);\n    if (pValue.type == ValueTypeError) return pValue.v.errorCode;\n    \n    int len = -1;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // length value\n        struct TypedValue lenValue = itp_evaluateNumericExpression(core, 0, 255);\n        if (lenValue.type == ValueTypeError) return lenValue.v.errorCode;\n        \n        len = lenValue.v.floatValue;\n    }\n    \n    int sound = -1;\n    if (interpreter->pc->type == TokenSOUND)\n    {\n        // SOUND\n        ++interpreter->pc;\n\n        // length value\n        struct TypedValue sValue = itp_evaluateNumericExpression(core, 0, NUM_SOUNDS - 1);\n        if (sValue.type == ValueTypeError) return sValue.v.errorCode;\n        \n        sound = sValue.v.floatValue;\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        audlib_play(&core->interpreter->audioLib, nValue.v.floatValue, pValue.v.floatValue, len, sound);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_STOP(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // STOP\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_VOICES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (nValue.type != ValueTypeNull)\n        {\n            int n = nValue.v.floatValue;\n            audlib_stopVoice(&interpreter->audioLib, n);\n        }\n        else\n        {\n            audlib_stopAll(&interpreter->audioLib);\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_MUSIC(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // MUSIC\n    ++interpreter->pc;\n    \n    // pattern value\n    struct TypedValue pValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_PATTERNS - 1);\n    if (pValue.type == ValueTypeError) return pValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int startPattern = (pValue.type != ValueTypeNull) ? pValue.v.floatValue : 0;\n        audlib_playMusic(&interpreter->audioLib, startPattern);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_TRACK(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // TRACK\n    ++interpreter->pc;\n    \n    // track value\n    struct TypedValue tValue = itp_evaluateNumericExpression(core, 0, NUM_TRACKS - 1);\n    if (tValue.type == ValueTypeError) return tValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // voice value\n    struct TypedValue vValue = itp_evaluateNumericExpression(core, 0, NUM_VOICES - 1);\n    if (vValue.type == ValueTypeError) return vValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        audlib_playTrack(&interpreter->audioLib, tValue.v.floatValue, vValue.v.floatValue);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_SOUND_SOURCE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SOUND\n    ++interpreter->pc;\n    \n    // SOURCE\n    ++interpreter->pc;\n    \n    // address value\n    struct TypedValue aValue = itp_evaluateNumericExpression(core, 0, 0xFFFF);\n    if (aValue.type == ValueTypeError) return aValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->audioLib.sourceAddress = aValue.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_MUSIC(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // MUSIC\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int x = xValue.v.floatValue;\n        struct ComposerPlayer *player = &interpreter->audioLib.musicPlayer;\n        switch (x)\n        {\n            case 0:\n                value.v.floatValue = player->index;\n                break;\n            case 1:\n                value.v.floatValue = player->row;\n                break;\n            case 2:\n                value.v.floatValue = player->tick;\n                break;\n            case 3:\n                value.v.floatValue = player->speed;\n                break;\n            default:\n                return val_makeError(ErrorInvalidParameter);\n        }\n    }\n    return value;\n}\n"
  },
  {
    "path": "core/interpreter/cmd_audio.h",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_audio_h\n#define cmd_audio_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"value.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_SOUND(struct Core *core);\n//enum ErrorCode cmd_SOUND_COPY(struct Core *core);\nenum ErrorCode cmd_VOLUME(struct Core *core);\nenum ErrorCode cmd_ENVELOPE(struct Core *core);\nenum ErrorCode cmd_LFO(struct Core *core);\nenum ErrorCode cmd_LFO_A(struct Core *core);\nenum ErrorCode cmd_LFO_WAVE(struct Core *core);\nenum ErrorCode cmd_PLAY(struct Core *core);\nenum ErrorCode cmd_STOP(struct Core *core);\nenum ErrorCode cmd_MUSIC(struct Core *core);\nenum ErrorCode cmd_TRACK(struct Core *core);\nenum ErrorCode cmd_SOUND_SOURCE(struct Core *core);\nstruct TypedValue fnc_MUSIC(struct Core *core);\n\n#endif /* cmd_audio_h */\n"
  },
  {
    "path": "core/interpreter/cmd_background.c",
    "content": "//\n// Copyright 2017-2019 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_background.h\"\n#include \"core.h\"\n#include \"text_lib.h\"\n#include \"cmd_text.h\"\n#include \"interpreter_utils.h\"\n#include <assert.h>\n#include <math.h>\n\nenum ErrorCode cmd_BG(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BG\n    ++interpreter->pc;\n    \n    // bg value\n    struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1);\n    if (bgValue.type == ValueTypeError) return bgValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->textLib.bg = bgValue.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_BG_SOURCE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BG SOURCE\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    // address value\n    struct TypedValue aValue = itp_evaluateNumericExpression(core, 0, 0xFFFF);\n    if (aValue.type == ValueTypeError) return aValue.v.errorCode;\n    \n    int w = 0;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // width value\n        struct TypedValue wValue = itp_evaluateNumericExpression(core, 1, 0xFFFF);\n        if (wValue.type == ValueTypeError) return wValue.v.errorCode;\n        \n        w = wValue.v.floatValue;\n    }\n    \n    int h = 0;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // height value\n        struct TypedValue hValue = itp_evaluateNumericExpression(core, 1, 0xFFFF);\n        if (hValue.type == ValueTypeError) return hValue.v.errorCode;\n        \n        h = hValue.v.floatValue;\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        int address = aValue.v.floatValue;\n        if (w > 0)\n        {\n            core->interpreter->textLib.sourceAddress = address;\n            core->interpreter->textLib.sourceWidth = w;\n            core->interpreter->textLib.sourceHeight = h;\n        }\n        else\n        {\n            // data with preceding size (W x H)\n            core->interpreter->textLib.sourceAddress = address + 4;\n            core->interpreter->textLib.sourceWidth = machine_peek(core, address + 2);\n            core->interpreter->textLib.sourceHeight = machine_peek(core, address + 3);\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_BG_COPY(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BG COPY\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    // src X value\n    struct TypedValue srcXValue = itp_evaluateNumericExpression(core, 0, 0xFFFF);\n    if (srcXValue.type == ValueTypeError) return srcXValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // src Y value\n    struct TypedValue srcYValue = itp_evaluateNumericExpression(core, 0, 0xFFFF);\n    if (srcYValue.type == ValueTypeError) return srcYValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // width value\n    struct TypedValue wValue = itp_evaluateNumericExpression(core, 0, PLANE_COLUMNS);\n    if (wValue.type == ValueTypeError) return wValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // height value\n    struct TypedValue hValue = itp_evaluateNumericExpression(core, 0, PLANE_ROWS);\n    if (hValue.type == ValueTypeError) return hValue.v.errorCode;\n    \n    // TO\n    if (interpreter->pc->type != TokenTO) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // dst X value\n    struct TypedValue dstXValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (dstXValue.type == ValueTypeError) return dstXValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // dst Y value\n    struct TypedValue dstYValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (dstYValue.type == ValueTypeError) return dstYValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        txtlib_copyBackground(&interpreter->textLib, srcXValue.v.floatValue, srcYValue.v.floatValue, wValue.v.floatValue, hValue.v.floatValue, dstXValue.v.floatValue, dstYValue.v.floatValue);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_BG_SCROLL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BG SCROLL\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    // x1 value\n    struct TypedValue x1Value = itp_evaluateNumericExpression(core, 0, PLANE_COLUMNS - 1);\n    if (x1Value.type == ValueTypeError) return x1Value.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y1 value\n    struct TypedValue y1Value = itp_evaluateNumericExpression(core, 0, PLANE_ROWS - 1);\n    if (y1Value.type == ValueTypeError) return y1Value.v.errorCode;\n    \n    // TO\n    if (interpreter->pc->type != TokenTO) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // x2 value\n    struct TypedValue x2Value = itp_evaluateNumericExpression(core, 0, PLANE_COLUMNS - 1);\n    if (x2Value.type == ValueTypeError) return x2Value.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y2 value\n    struct TypedValue y2Value = itp_evaluateNumericExpression(core, 0, PLANE_ROWS - 1);\n    if (y2Value.type == ValueTypeError) return y2Value.v.errorCode;\n\n    // STEP\n    if (interpreter->pc->type != TokenSTEP) return ErrorSyntax;\n    ++interpreter->pc;\n\n    // dx value\n    struct TypedValue dxValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (dxValue.type == ValueTypeError) return dxValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // dy value\n    struct TypedValue dyValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (dyValue.type == ValueTypeError) return dyValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        txtlib_scrollBackground(&interpreter->textLib, x1Value.v.floatValue, y1Value.v.floatValue, x2Value.v.floatValue, y2Value.v.floatValue, dxValue.v.floatValue, dyValue.v.floatValue);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_ATTR(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // ATTR\n    ++interpreter->pc;\n    \n    // attributes value\n    struct TypedValue aValue = itp_evaluateCharAttributes(core, interpreter->textLib.charAttr);\n    if (aValue.type == ValueTypeError) return aValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->textLib.charAttr.value = aValue.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_PAL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // PAL\n    ++interpreter->pc;\n    \n    // value\n    struct TypedValue value = itp_evaluateNumericExpression(core, 0, NUM_PALETTES - 1);\n    if (value.type == ValueTypeError) return value.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->textLib.charAttr.palette = value.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_FLIP(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // FLIP\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue fxValue = itp_evaluateNumericExpression(core, -1, 1);\n    if (fxValue.type == ValueTypeError) return fxValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue fyValue = itp_evaluateNumericExpression(core, -1, 1);\n    if (fyValue.type == ValueTypeError) return fyValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->textLib.charAttr.flipX = fxValue.v.floatValue ? 1 : 0;\n        interpreter->textLib.charAttr.flipY = fyValue.v.floatValue ? 1 : 0;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_PRIO(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // PRIO\n    ++interpreter->pc;\n    \n    // value\n    struct TypedValue value = itp_evaluateNumericExpression(core, -1, 1);\n    if (value.type == ValueTypeError) return value.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->textLib.charAttr.priority = value.v.floatValue ? 1 : 0;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_BG_FILL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BG FILL\n    ++interpreter->pc;\n    ++interpreter->pc;\n\n    // x1 value\n    struct TypedValue x1Value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (x1Value.type == ValueTypeError) return x1Value.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y1 value\n    struct TypedValue y1Value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (y1Value.type == ValueTypeError) return y1Value.v.errorCode;\n    \n    // TO\n    if (interpreter->pc->type != TokenTO) return ErrorSyntax;\n    ++interpreter->pc;\n\n    // x2 value\n    struct TypedValue x2Value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (x2Value.type == ValueTypeError) return x2Value.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y2 value\n    struct TypedValue y2Value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (y2Value.type == ValueTypeError) return y2Value.v.errorCode;\n\n    // CHAR\n    if (interpreter->pc->type == TokenCHAR)\n    {\n        ++interpreter->pc;\n        \n        // write character with current attributes\n    \n        // character value\n        struct TypedValue cValue = itp_evaluateNumericExpression(core, 0, NUM_CHARACTERS - 1);\n        if (cValue.type == ValueTypeError) return cValue.v.errorCode;\n        \n        if (interpreter->pass == PassRun)\n        {\n            txtlib_setCells(&interpreter->textLib, floorf(x1Value.v.floatValue), floorf(y1Value.v.floatValue), floorf(x2Value.v.floatValue), floorf(y2Value.v.floatValue), cValue.v.floatValue);\n        }\n    }\n    else\n    {\n        // write current attributes (obsolete syntax!)\n        \n        if (interpreter->pass == PassRun)\n        {\n            txtlib_setCells(&interpreter->textLib, floorf(x1Value.v.floatValue), floorf(y1Value.v.floatValue), floorf(x2Value.v.floatValue), floorf(y2Value.v.floatValue), -1);\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_BG_TINT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BG TINT\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    // x1 value\n    struct TypedValue x1Value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (x1Value.type == ValueTypeError) return x1Value.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y1 value\n    struct TypedValue y1Value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (y1Value.type == ValueTypeError) return y1Value.v.errorCode;\n    \n    // TO\n    if (interpreter->pc->type != TokenTO) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // x2 value\n    struct TypedValue x2Value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (x2Value.type == ValueTypeError) return x2Value.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y2 value\n    struct TypedValue y2Value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (y2Value.type == ValueTypeError) return y2Value.v.errorCode;\n    \n    struct SimpleAttributes attrs;\n    enum ErrorCode attrsError = itp_evaluateSimpleAttributes(core, &attrs);\n    if (attrsError != ErrorNone) return attrsError;\n    \n    if (interpreter->pass == PassRun)\n    {\n        txtlib_setCellsAttr(&interpreter->textLib, floorf(x1Value.v.floatValue), floorf(y1Value.v.floatValue), floorf(x2Value.v.floatValue), floorf(y2Value.v.floatValue), attrs.pal, attrs.flipX, attrs.flipY, attrs.prio);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_CELL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // CELL\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // character value\n    struct TypedValue cValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_CHARACTERS - 1);\n    if (cValue.type == ValueTypeError) return cValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int c = (cValue.type == ValueTypeFloat) ? cValue.v.floatValue : -1;\n        txtlib_setCell(&interpreter->textLib, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue), c);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_CELL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // CELL.?\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (yValue.type == ValueTypeError) return yValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        struct Cell *cell = txtlib_getCell(&interpreter->textLib, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue));\n        if (type == TokenCELLA)\n        {\n            value.v.floatValue = cell->attr.value;\n        }\n        else if (type == TokenCELLC)\n        {\n            value.v.floatValue = cell->character;\n        }\n        else\n        {\n            assert(0);\n        }\n    }\n    return value;\n}\n\nenum ErrorCode cmd_MCELL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // MCELL\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateNumericExpression(core, 0, interpreter->textLib.sourceWidth - 1);\n    if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateNumericExpression(core, 0, interpreter->textLib.sourceHeight - 1);\n    if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // character value\n    struct TypedValue cValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_CHARACTERS - 1);\n    if (cValue.type == ValueTypeError) return cValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int c = (cValue.type == ValueTypeFloat) ? cValue.v.floatValue : -1;\n        bool success = txtlib_setSourceCell(&interpreter->textLib, xValue.v.floatValue, yValue.v.floatValue, c);\n        if (!success) return ErrorIllegalMemoryAccess;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_MCELL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // MCELL.?\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n        ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax);\n        ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (yValue.type == ValueTypeError) return yValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n        ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int x = floorf(xValue.v.floatValue);\n        int y = floorf(yValue.v.floatValue);\n        value.v.floatValue = txtlib_getSourceCell(&interpreter->textLib, x, y, (type == TokenMCELLA));\n    }\n    return value;\n}\n\nenum ErrorCode cmd_TINT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // TINT\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n    \n    struct SimpleAttributes attrs;\n    enum ErrorCode attrsError = itp_evaluateSimpleAttributes(core, &attrs);\n    if (attrsError != ErrorNone) return attrsError;\n    \n    if (interpreter->pass == PassRun)\n    {\n        struct Cell *cell = txtlib_getCell(&interpreter->textLib, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue));\n        if (attrs.pal >= 0) cell->attr.palette = attrs.pal;\n        if (attrs.flipX >= 0) cell->attr.flipX = attrs.flipX;\n        if (attrs.flipY >= 0) cell->attr.flipY = attrs.flipY;\n        if (attrs.prio >= 0) cell->attr.priority = attrs.prio;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n"
  },
  {
    "path": "core/interpreter/cmd_background.h",
    "content": "//\n// Copyright 2017-2019 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_background_h\n#define cmd_background_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"value.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_BG(struct Core *core);\nenum ErrorCode cmd_BG_SOURCE(struct Core *core);\nenum ErrorCode cmd_BG_COPY(struct Core *core);\nenum ErrorCode cmd_BG_SCROLL(struct Core *core);\nenum ErrorCode cmd_ATTR(struct Core *core);\nenum ErrorCode cmd_PAL(struct Core *core);\nenum ErrorCode cmd_FLIP(struct Core *core);\nenum ErrorCode cmd_PRIO(struct Core *core);\nenum ErrorCode cmd_BG_FILL(struct Core *core);\nenum ErrorCode cmd_BG_TINT(struct Core *core);\nenum ErrorCode cmd_CELL(struct Core *core);\nstruct TypedValue fnc_CELL(struct Core *core);\nenum ErrorCode cmd_MCELL(struct Core *core);\nstruct TypedValue fnc_MCELL(struct Core *core);\nenum ErrorCode cmd_TINT(struct Core *core);\n\n#endif /* cmd_background_h */\n"
  },
  {
    "path": "core/interpreter/cmd_control.c",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_control.h\"\n#include \"core.h\"\n#include <assert.h>\n\nenum ErrorCode cmd_END(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    // END\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassRun)\n    {\n        itp_endProgram(core);\n        return ErrorNone;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_IF(struct Core *core, bool isAfterBlockElse)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // IF\n    struct Token *tokenIF = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Expression\n    struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (value.type == ValueTypeError) return value.v.errorCode;\n    \n    // THEN\n    if (interpreter->pc->type != TokenTHEN) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        if (interpreter->pc->type == TokenEol)\n        {\n            // IF block\n            if (interpreter->isSingleLineIf) return ErrorExpectedCommand;\n            enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, isAfterBlockElse ? LabelTypeELSEIF : LabelTypeIF, tokenIF);\n            if (errorCode != ErrorNone) return errorCode;\n            \n            // Eol\n            ++interpreter->pc;\n        }\n        else\n        {\n            // single line IF\n            interpreter->isSingleLineIf = true;\n            struct Token *token = interpreter->pc;\n            while (token->type != TokenEol && token->type != TokenELSE)\n            {\n                token++;\n            }\n            tokenIF->jumpToken = token + 1; // after ELSE or Eol\n        }\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        if (value.v.floatValue == 0)\n        {\n            interpreter->pc = tokenIF->jumpToken; // after ELSE or END IF, or Eol for single line\n        }\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_ELSE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // ELSE\n    struct Token *tokenELSE = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        if (interpreter->isSingleLineIf)\n        {\n            if (interpreter->pc->type == TokenEol) return ErrorExpectedCommand;\n            struct Token *token = interpreter->pc;\n            while (token->type != TokenEol)\n            {\n                token++;\n            }\n            tokenELSE->jumpToken = token + 1; // after Eol\n        }\n        else\n        {\n            struct LabelStackItem *item = lab_popLabelStackItem(interpreter);\n            if (!item) return ErrorElseWithoutIf;\n            if (item->type == LabelTypeIF)\n            {\n                item->token->jumpToken = interpreter->pc;\n            }\n            else if (item->type == LabelTypeELSEIF)\n            {\n                item->token->jumpToken = interpreter->pc;\n                \n                item = lab_popLabelStackItem(interpreter);\n                assert(item->type == LabelTypeELSE);\n                item->token->jumpToken = tokenELSE;\n            }\n            else\n            {\n                return ErrorElseWithoutIf;\n            }\n            \n            enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeELSE, tokenELSE);\n            if (errorCode != ErrorNone) return errorCode;\n            \n            if (interpreter->pc->type == TokenIF)\n            {\n                return cmd_IF(core, true);\n            }\n            else\n            {\n                // Eol\n                if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n                ++interpreter->pc;\n            }\n        }\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        interpreter->pc = tokenELSE->jumpToken; // after END IF, or Eol for single line\n    }\n    return ErrorNone;\n}\n\nenum ErrorCode cmd_END_IF(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // END IF\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        struct LabelStackItem *item = lab_popLabelStackItem(interpreter);\n        if (!item)\n        {\n            return ErrorEndIfWithoutIf;\n        }\n        else if (item->type == LabelTypeIF || item->type == LabelTypeELSE)\n        {\n            item->token->jumpToken = interpreter->pc;\n        }\n        else if (item->type == LabelTypeELSEIF)\n        {\n            item->token->jumpToken = interpreter->pc;\n            \n            item = lab_popLabelStackItem(interpreter);\n            assert(item->type == LabelTypeELSE);\n            item->token->jumpToken = interpreter->pc;\n        }\n        else\n        {\n            return ErrorEndIfWithoutIf;\n        }\n    }\n    \n    // Eol\n    if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_FOR(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // FOR\n    struct Token *tokenFOR = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Variable\n    struct Token *tokenFORVar = interpreter->pc;\n    enum ErrorCode errorCode = ErrorNone;\n    enum ValueType valueType = ValueTypeNull;\n    union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true);\n    if (!varValue) return errorCode;\n    if (valueType != ValueTypeFloat) return ErrorTypeMismatch;\n    \n    // Eq\n    if (interpreter->pc->type != TokenEq) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // start value\n    struct TypedValue startValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (startValue.type == ValueTypeError) return startValue.v.errorCode;\n    \n    // TO\n    if (interpreter->pc->type != TokenTO) return ErrorSyntax;\n    ++interpreter->pc;\n\n    // limit value\n    struct Token *tokenFORLimit = interpreter->pc;\n    struct TypedValue limitValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (limitValue.type == ValueTypeError) return limitValue.v.errorCode;\n    \n    // STEP\n    struct TypedValue stepValue;\n    if (interpreter->pc->type == TokenSTEP)\n    {\n        ++interpreter->pc;\n        \n        // step value\n        stepValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (stepValue.type == ValueTypeError) return stepValue.v.errorCode;\n    }\n    else\n    {\n        stepValue.type = ValueTypeFloat;\n        stepValue.v.floatValue = 1.0f;\n    }\n        \n    if (interpreter->pass == PassPrepare)\n    {\n        lab_pushLabelStackItem(interpreter, LabelTypeFORLimit, tokenFORLimit);\n        lab_pushLabelStackItem(interpreter, LabelTypeFORVar, tokenFORVar);\n        enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeFOR, tokenFOR);\n        if (errorCode != ErrorNone) return errorCode;\n        \n        // Eol\n        if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        varValue->floatValue = startValue.v.floatValue;\n        \n        // limit check\n        if ((stepValue.v.floatValue > 0 && varValue->floatValue > limitValue.v.floatValue) || (stepValue.v.floatValue < 0 && varValue->floatValue < limitValue.v.floatValue))\n        {\n            interpreter->pc = tokenFOR->jumpToken; // after NEXT's Eol\n        }\n        else\n        {\n            // Eol\n            if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n            ++interpreter->pc;\n        }\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_NEXT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    struct LabelStackItem *itemFORLimit = NULL;\n    struct LabelStackItem *itemFORVar = NULL;\n    struct LabelStackItem *itemFOR = NULL;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        itemFOR = lab_popLabelStackItem(interpreter);\n        if (!itemFOR || itemFOR->type != LabelTypeFOR) return ErrorNextWithoutFor;\n        \n        itemFORVar = lab_popLabelStackItem(interpreter);\n        assert(itemFORVar && itemFORVar->type == LabelTypeFORVar);\n        \n        itemFORLimit = lab_popLabelStackItem(interpreter);\n        assert(itemFORLimit && itemFORLimit->type == LabelTypeFORLimit);\n    }\n    \n    // NEXT\n    struct Token *tokenNEXT = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Variable\n    enum ErrorCode errorCode = ErrorNone;\n    struct Token *tokenVar = interpreter->pc;\n    enum ValueType valueType = ValueTypeNull;\n    union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true);\n    if (!varValue) return errorCode;\n    if (valueType != ValueTypeFloat) return ErrorTypeMismatch;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        if (tokenVar->symbolIndex != itemFORVar->token->symbolIndex) return ErrorNextWithoutFor;\n    }\n    \n    // Eol\n    if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        itemFOR->token->jumpToken = interpreter->pc;\n        tokenNEXT->jumpToken = itemFORLimit->token;\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        struct Token *storedPc = interpreter->pc;\n        interpreter->pc = tokenNEXT->jumpToken;\n        \n        // limit value\n        struct TypedValue limitValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (limitValue.type == ValueTypeError) return limitValue.v.errorCode;\n        \n        // STEP\n        struct TypedValue stepValue;\n        if (interpreter->pc->type == TokenSTEP)\n        {\n            ++interpreter->pc;\n            \n            // step value\n            stepValue = itp_evaluateExpression(core, TypeClassNumeric);\n            if (stepValue.type == ValueTypeError) return stepValue.v.errorCode;\n        }\n        else\n        {\n            stepValue.type = ValueTypeFloat;\n            stepValue.v.floatValue = 1.0f;\n        }\n        \n        // Eol\n        if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n        ++interpreter->pc;\n\n        varValue->floatValue += stepValue.v.floatValue;\n        \n        // limit check\n        if ((stepValue.v.floatValue > 0 && varValue->floatValue > limitValue.v.floatValue) || (stepValue.v.floatValue < 0 && varValue->floatValue < limitValue.v.floatValue))\n        {\n            interpreter->pc = storedPc; // after NEXT's Eol\n        }\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_GOTO(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // GOTO\n    struct Token *tokenGOTO = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Identifier\n    if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedLabel;\n    struct Token *tokenIdentifier = interpreter->pc;\n    ++interpreter->pc;\n\n    if (interpreter->pass == PassPrepare)\n    {\n        struct JumpLabelItem *item = tok_getJumpLabel(&interpreter->tokenizer, tokenIdentifier->symbolIndex);\n        if (!item) return ErrorUndefinedLabel;\n        tokenGOTO->jumpToken = item->token;\n        \n        return itp_endOfCommand(interpreter);\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        interpreter->pc = tokenGOTO->jumpToken; // after label\n    }\n    return ErrorNone;\n}\n\nenum ErrorCode cmd_GOSUB(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // GOSUB\n    struct Token *tokenGOSUB = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Identifier\n    if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedLabel;\n    struct Token *tokenIdentifier = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        struct JumpLabelItem *item = tok_getJumpLabel(&interpreter->tokenizer, tokenIdentifier->symbolIndex);\n        if (!item) return ErrorUndefinedLabel;\n        tokenGOSUB->jumpToken = item->token;\n        \n        return itp_endOfCommand(interpreter);\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeGOSUB, interpreter->pc);\n        if (errorCode != ErrorNone) return errorCode;\n        \n        interpreter->pc = tokenGOSUB->jumpToken; // after label\n    }\n    return ErrorNone;\n}\n\nenum ErrorCode cmd_RETURN(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // RETURN\n    struct Token *tokenRETURN = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Identifier\n    struct Token *tokenIdentifier = NULL;\n    if (interpreter->pc->type == TokenIdentifier)\n    {\n        tokenIdentifier = interpreter->pc;\n        ++interpreter->pc;\n    }\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        if (tokenIdentifier)\n        {\n            struct JumpLabelItem *item = tok_getJumpLabel(&interpreter->tokenizer, tokenIdentifier->symbolIndex);\n            if (!item) return ErrorUndefinedLabel;\n            tokenRETURN->jumpToken = item->token;\n        }\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        struct LabelStackItem *itemGOSUB = lab_popLabelStackItem(interpreter);\n        if (!itemGOSUB) return ErrorReturnWithoutGosub;\n        \n        if (itemGOSUB->type == LabelTypeGOSUB)\n        {\n            if (tokenRETURN->jumpToken)\n            {\n                // jump to label\n                interpreter->pc = tokenRETURN->jumpToken; // after label\n                // clear stack\n                interpreter->numLabelStackItems = 0;\n            }\n            else\n            {\n                // jump back\n                interpreter->pc = itemGOSUB->token; // after GOSUB\n            }\n        }\n        else\n        {\n            return ErrorReturnWithoutGosub;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_WAIT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    // WAIT\n    ++interpreter->pc;\n    \n    int wait = 0;\n    if (interpreter->pc->type == TokenVBL)\n    {\n        // VBL\n        ++interpreter->pc;\n    }\n    else\n    {\n        // value\n        struct TypedValue value = itp_evaluateNumericExpression(core, 1, 0xFFFF);\n        if (value.type == ValueTypeError) return value.v.errorCode;\n        wait = value.v.floatValue - 1;\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->exitEvaluation = true;\n        interpreter->waitCount = wait;\n    }\n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_ON(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // ON\n    ++interpreter->pc;\n    \n    // RASTER/VBL\n    enum TokenType type = interpreter->pc->type;\n    if (type != TokenRASTER && type != TokenVBL) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    if (interpreter->pc->type == TokenOFF)\n    {\n        // OFF\n        ++interpreter->pc;\n        \n        if (interpreter->pass == PassRun)\n        {\n            if (type == TokenRASTER)\n            {\n                interpreter->currentOnRasterToken = NULL;\n            }\n            else if (type == TokenVBL)\n            {\n                interpreter->currentOnVBLToken = NULL;\n            }\n        }\n    }\n    else\n    {\n        // CALL\n        if (interpreter->pc->type != TokenCALL) return ErrorSyntax;\n        struct Token *tokenCALL = interpreter->pc;\n        ++interpreter->pc;\n        \n        // Identifier\n        if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedSubprogramName;\n        struct Token *tokenIdentifier = interpreter->pc;\n        ++interpreter->pc;\n        \n        if (interpreter->pass == PassPrepare)\n        {\n            struct SubItem *item = tok_getSub(&interpreter->tokenizer, tokenIdentifier->symbolIndex);\n            if (!item) return ErrorUndefinedSubprogram;\n            tokenCALL->jumpToken = item->token;\n        }\n        else if (interpreter->pass == PassRun)\n        {\n            if (type == TokenRASTER)\n            {\n                interpreter->currentOnRasterToken = tokenCALL->jumpToken;\n            }\n            else if (type == TokenVBL)\n            {\n                interpreter->currentOnVBLToken = tokenCALL->jumpToken;\n            }\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_DO(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // DO\n    struct Token *tokenDO = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeDO, tokenDO);\n        if (errorCode != ErrorNone) return errorCode;\n    }\n    \n    // Eol\n    if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_LOOP(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // LOOP\n    struct Token *tokenLOOP = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        struct LabelStackItem *item = lab_popLabelStackItem(interpreter);\n        if (!item || item->type != LabelTypeDO) return ErrorLoopWithoutDo;\n        \n        tokenLOOP->jumpToken = item->token + 1;\n        item->token->jumpToken = tokenLOOP + 1;\n        \n        // Eol\n        if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        interpreter->pc = tokenLOOP->jumpToken; // after DO\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_REPEAT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // REPEAT\n    struct Token *tokenREPEAT = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeREPEAT, tokenREPEAT);\n        if (errorCode != ErrorNone) return errorCode;\n    }\n    \n    // Eol\n    if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_UNTIL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // UNTIL\n    struct Token *tokenUNTIL = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Expression\n    struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (value.type == ValueTypeError) return value.v.errorCode;\n        \n    if (interpreter->pass == PassPrepare)\n    {\n        struct LabelStackItem *item = lab_popLabelStackItem(interpreter);\n        if (!item || item->type != LabelTypeREPEAT) return ErrorUntilWithoutRepeat;\n        \n        tokenUNTIL->jumpToken = item->token + 1;\n        item->token->jumpToken = interpreter->pc;\n        \n        // Eol\n        if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        if (value.v.floatValue == 0)\n        {\n            interpreter->pc = tokenUNTIL->jumpToken; // after REPEAT\n        }\n        else\n        {\n            // Eol\n            if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n            ++interpreter->pc;\n        }\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_WHILE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // WHILE\n    struct Token *tokenWHILE = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeWHILE, tokenWHILE);\n        if (errorCode != ErrorNone) return errorCode;\n    }\n    \n    // Expression\n    struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (value.type == ValueTypeError) return value.v.errorCode;\n    \n    // Eol\n    if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (value.v.floatValue == 0)\n        {\n            interpreter->pc = tokenWHILE->jumpToken; // after WEND\n        }\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_WEND(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // WEND\n    struct Token *tokenWEND = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        struct LabelStackItem *item = lab_popLabelStackItem(interpreter);\n        if (!item || item->type != LabelTypeWHILE) return ErrorWendWithoutWhile;\n        \n        tokenWEND->jumpToken = item->token;\n        item->token->jumpToken = tokenWEND + 1;\n        \n        // Eol\n        if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        interpreter->pc = tokenWEND->jumpToken; // on WHILE\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_EXIT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // EXIT\n    struct Token *tokenEXIT = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        enum LabelType types[] = {LabelTypeFOR, LabelTypeDO, LabelTypeWHILE, LabelTypeREPEAT};\n        struct LabelStackItem *item = lab_searchLabelStackItem(interpreter, types, 4);\n        if (!item) return ErrorExitNotInsideLoop;\n        \n        tokenEXIT->jumpToken = item->token;\n        \n        return itp_endOfCommand(interpreter);\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        interpreter->pc = tokenEXIT->jumpToken->jumpToken;\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_SYSTEM(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SYSTEM\n    ++interpreter->pc;\n    \n    // type value\n    struct TypedValue tValue = itp_evaluateNumericExpression(core, 0, 0);\n    if (tValue.type == ValueTypeError) return tValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // setting value\n    struct TypedValue sValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (sValue.type == ValueTypeError) return sValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        switch ((int)tValue.v.floatValue)\n        {\n            case 0:\n                core->machineInternals->isEnergySaving = (sValue.v.floatValue != 0.0f);\n                break;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n"
  },
  {
    "path": "core/interpreter/cmd_control.h",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_control_h\n#define cmd_control_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"error.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_END(struct Core *core);\nenum ErrorCode cmd_IF(struct Core *core, bool isAfterBlockElse);\nenum ErrorCode cmd_ELSE(struct Core *core);\nenum ErrorCode cmd_END_IF(struct Core *core);\nenum ErrorCode cmd_FOR(struct Core *core);\nenum ErrorCode cmd_NEXT(struct Core *core);\nenum ErrorCode cmd_GOTO(struct Core *core);\nenum ErrorCode cmd_GOSUB(struct Core *core);\nenum ErrorCode cmd_RETURN(struct Core *core);\nenum ErrorCode cmd_WAIT(struct Core *core);\nenum ErrorCode cmd_ON(struct Core *core);\nenum ErrorCode cmd_DO(struct Core *core);\nenum ErrorCode cmd_LOOP(struct Core *core);\nenum ErrorCode cmd_REPEAT(struct Core *core);\nenum ErrorCode cmd_UNTIL(struct Core *core);\nenum ErrorCode cmd_WHILE(struct Core *core);\nenum ErrorCode cmd_WEND(struct Core *core);\nenum ErrorCode cmd_EXIT(struct Core *core);\nenum ErrorCode cmd_SYSTEM(struct Core *core);\n\n#endif /* cmd_control_h */\n"
  },
  {
    "path": "core/interpreter/cmd_data.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_data.h\"\n#include \"core.h\"\n\nenum ErrorCode cmd_DATA(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        if (!interpreter->firstData)\n        {\n            interpreter->firstData = interpreter->pc;\n        }\n        if (interpreter->lastData)\n        {\n            interpreter->lastData->jumpToken = interpreter->pc;\n        }\n        interpreter->lastData = interpreter->pc;\n    }\n    \n    do\n    {\n        ++interpreter->pc; // DATA at first, then comma\n        \n        if (interpreter->pc->type == TokenString)\n        {\n            ++interpreter->pc;\n        }\n        else if (interpreter->pc->type == TokenFloat)\n        {\n            ++interpreter->pc;\n        }\n        else if (interpreter->pc->type == TokenMinus)\n        {\n            ++interpreter->pc;\n            if (interpreter->pc->type != TokenFloat) return ErrorSyntax;\n            ++interpreter->pc;\n        }\n        else\n        {\n            return ErrorSyntax;\n        }\n    }\n    while (interpreter->pc->type == TokenComma);\n    \n    // Eol\n    if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_READ(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    do\n    {\n         // READ at first, then comma\n        ++interpreter->pc;\n        \n        // variable\n        enum ValueType varType = ValueTypeNull;\n        enum ErrorCode errorCode = ErrorNone;\n        union Value *varValue = itp_readVariable(core, &varType, &errorCode, true);\n        if (!varValue) return errorCode;\n            \n        if (interpreter->pass == PassRun)\n        {\n            if (!interpreter->currentDataValueToken) return ErrorOutOfData;\n            \n            struct Token *dataValueToken = interpreter->currentDataValueToken;\n            if (dataValueToken->type == TokenFloat)\n            {\n                if (varType != ValueTypeFloat) return ErrorTypeMismatch;\n                varValue->floatValue = dataValueToken->floatValue;\n            }\n            else if (dataValueToken->type == TokenMinus)\n            {\n                if (varType != ValueTypeFloat) return ErrorTypeMismatch;\n                interpreter->currentDataValueToken++;\n                varValue->floatValue = -interpreter->currentDataValueToken->floatValue;\n            }\n            else if (dataValueToken->type == TokenString)\n            {\n                if (varType != ValueTypeString) return ErrorTypeMismatch;\n                if (varValue->stringValue)\n                {\n                    rcstring_release(varValue->stringValue);\n                }\n                varValue->stringValue = dataValueToken->stringValue;\n                rcstring_retain(varValue->stringValue);\n            }\n            \n            dat_nextData(interpreter);\n        }\n    }\n    while (interpreter->pc->type == TokenComma);\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_RESTORE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // RESTORE\n    struct Token *tokenRESTORE = interpreter->pc;\n    ++interpreter->pc;\n    \n    // optional jump label\n    if (interpreter->pc->type == TokenIdentifier)\n    {\n        if (interpreter->pass == PassPrepare)\n        {\n            struct JumpLabelItem *item = tok_getJumpLabel(&interpreter->tokenizer, interpreter->pc->symbolIndex);\n            if (!item) return ErrorUndefinedLabel;\n            tokenRESTORE->jumpToken = item->token;\n        }\n        else if (interpreter->pass == PassRun)\n        {\n            // find DATA after label\n            dat_restoreData(interpreter, tokenRESTORE->jumpToken);\n        }\n        ++interpreter->pc;\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        // restore to first DATA\n        dat_restoreData(interpreter, NULL);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n"
  },
  {
    "path": "core/interpreter/cmd_data.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_data_h\n#define cmd_data_h\n\n#include <stdio.h>\n#include \"error.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_DATA(struct Core *core);\nenum ErrorCode cmd_READ(struct Core *core);\nenum ErrorCode cmd_RESTORE(struct Core *core);\n\n#endif /* cmd_data_h */\n"
  },
  {
    "path": "core/interpreter/cmd_files.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_files.h\"\n#include \"core.h\"\n#include <assert.h>\n#include <string.h>\n\nenum ErrorCode cmd_LOAD(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n\n    // LOAD\n    struct Token *startPc = interpreter->pc;\n    ++interpreter->pc;\n    \n    // file value\n    struct TypedValue fileValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1);\n    if (fileValue.type == ValueTypeError) return fileValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // address value\n    struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (addressValue.type == ValueTypeError) return addressValue.v.errorCode;\n    \n    int maxLength = 0;\n    int offset = 0;\n    if (interpreter->pc->type == TokenComma)\n    {\n        ++interpreter->pc;\n        \n        // max length value\n        struct TypedValue maxLengthValue = itp_evaluateNumericExpression(core, 0, DATA_SIZE);\n        if (maxLengthValue.type == ValueTypeError) return maxLengthValue.v.errorCode;\n        maxLength = maxLengthValue.v.floatValue;\n        \n        if (interpreter->pc->type == TokenComma)\n        {\n            ++interpreter->pc;\n            \n            // offset value\n            struct TypedValue offsetValue = itp_evaluateNumericExpression(core, 0, DATA_SIZE);\n            if (offsetValue.type == ValueTypeError) return offsetValue.v.errorCode;\n            offset = offsetValue.v.floatValue;\n        }\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        bool pokeFailed = false;\n        bool ready = disk_loadFile(core, fileValue.v.floatValue, addressValue.v.floatValue, maxLength, offset, &pokeFailed);\n        if (pokeFailed) return ErrorIllegalMemoryAccess;\n        \n        interpreter->exitEvaluation = true;\n        if (!ready)\n        {\n            // disk not ready\n            interpreter->pc = startPc;\n            interpreter->state = StateWaitForDisk;\n            return ErrorNone;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_SAVE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    // SAVE\n    struct Token *startPc = interpreter->pc;\n    ++interpreter->pc;\n    \n    // file value\n    struct TypedValue fileValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1);\n    if (fileValue.type == ValueTypeError) return fileValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // comment value\n    struct TypedValue commentValue = itp_evaluateExpression(core, TypeClassString);\n    if (commentValue.type == ValueTypeError) return commentValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // address value\n    struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (addressValue.type == ValueTypeError) return addressValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // length value\n    struct TypedValue lengthValue = itp_evaluateNumericExpression(core, 1, DATA_SIZE);\n    if (lengthValue.type == ValueTypeError) return lengthValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int address = addressValue.v.floatValue;\n        int length = lengthValue.v.floatValue;\n        if (address + length > 0x10000)\n        {\n            return ErrorIllegalMemoryAccess;\n        }\n        bool ready = disk_saveFile(core, fileValue.v.floatValue, commentValue.v.stringValue->chars, address, length);\n        rcstring_release(commentValue.v.stringValue);\n        \n        interpreter->exitEvaluation = true;\n        if (!ready)\n        {\n            // disk not ready\n            interpreter->pc = startPc;\n            interpreter->state = StateWaitForDisk;\n            return ErrorNone;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_FILES(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    // FILES\n    struct Token *startPc = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassRun)\n    {\n        bool ready = disk_prepare(core);\n        \n        interpreter->exitEvaluation = true;\n        if (!ready)\n        {\n            // disk not ready\n            interpreter->pc = startPc;\n            interpreter->state = StateWaitForDisk;\n            return ErrorNone;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_FILE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // FILE$\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // file value\n    struct TypedValue fileValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1);\n    if (fileValue.type == ValueTypeError) return fileValue;\n\n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n\n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeString;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (core->diskDrive->dataManager.data == NULL) return val_makeError(ErrorDirectoryNotLoaded);\n        \n        int index = fileValue.v.floatValue;\n        struct DataEntry *entry = &core->diskDrive->dataManager.entries[index];\n        \n        size_t len = strlen(entry->comment);\n        resultValue.v.stringValue = rcstring_new(entry->comment, len);\n        rcstring_retain(resultValue.v.stringValue);\n        interpreter->cycles += len;\n    }\n    return resultValue;\n}\n\nstruct TypedValue fnc_FSIZE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // FSIZE\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // file value\n    struct TypedValue fileValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1);\n    if (fileValue.type == ValueTypeError) return fileValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (core->diskDrive->dataManager.data == NULL) return val_makeError(ErrorDirectoryNotLoaded);\n        \n        int index = fileValue.v.floatValue;\n        struct DataEntry *entry = &core->diskDrive->dataManager.entries[index];\n        \n        resultValue.v.floatValue = entry->length;\n    }\n    return resultValue;\n}\n"
  },
  {
    "path": "core/interpreter/cmd_files.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_files_h\n#define cmd_files_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"value.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_LOAD(struct Core *core);\nenum ErrorCode cmd_SAVE(struct Core *core);\nenum ErrorCode cmd_FILES(struct Core *core);\nstruct TypedValue fnc_FILE(struct Core *core);\nstruct TypedValue fnc_FSIZE(struct Core *core);\n\n#endif /* cmd_files_h */\n"
  },
  {
    "path": "core/interpreter/cmd_io.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_io.h\"\n#include \"core.h\"\n#include <assert.h>\n\nenum ErrorCode cmd_KEYBOARD(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // KEYBOARD\n    ++interpreter->pc;\n    \n    // ON/OFF/OPTIONAL\n    enum TokenType type = interpreter->pc->type;\n    if (type != TokenON && type != TokenOFF && type != TokenOPTIONAL) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassRun)\n    {\n        core->machine->ioRegisters.attr.keyboardEnabled = (type == TokenON || type == TokenOPTIONAL);\n        interpreter->isKeyboardOptional = (type == TokenOPTIONAL);\n        delegate_controlsDidChange(core);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_TOUCHSCREEN(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // TOUCHSCREEN\n    ++interpreter->pc;\n        \n    if (interpreter->pass == PassRun)\n    {\n        if (core->machine->ioRegisters.attr.gamepadsEnabled > 0) return ErrorInputChangeNotAllowed;\n        core->machine->ioRegisters.attr.touchEnabled = 1;\n        delegate_controlsDidChange(core);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_GAMEPAD(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // GAMEPAD\n    ++interpreter->pc;\n    \n    // number\n    struct TypedValue value = itp_evaluateNumericExpression(core, 1, 2);\n    if (value.type == ValueTypeError) return value.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (core->machine->ioRegisters.attr.touchEnabled) return ErrorInputChangeNotAllowed;\n        \n        core->machine->ioRegisters.attr.gamepadsEnabled = value.v.floatValue;\n        core->machine->ioRegisters.status.touch = 0;\n        for (int i = 0; i < NUM_GAMEPADS; i++)\n        {\n            core->machine->ioRegisters.gamepads[i].value = 0;\n        }\n        delegate_controlsDidChange(core);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_PAUSE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // PAUSE\n    ++interpreter->pc;\n    \n    // ON/OFF?\n    enum TokenType type = interpreter->pc->type;\n    if (type == TokenON || type == TokenOFF)\n    {\n        ++interpreter->pc;\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (type == TokenON)\n        {\n            core->machine->ioRegisters.status.pause = 0;\n            interpreter->handlesPause = true;\n        }\n        else if (type == TokenOFF)\n        {\n            interpreter->handlesPause = false;\n        }\n        else\n        {\n            interpreter->state = StatePaused;\n            overlay_updateState(core);\n        }\n    }\n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_UP_DOWN_LEFT_RIGHT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // UP/DOWN/LEFT/RIGHT\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // TAP\n    bool tap = false;\n    if (interpreter->pc->type == TokenTAP)\n    {\n        ++interpreter->pc;\n        tap = true;\n    }\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // p expression\n    struct TypedValue pValue = itp_evaluateNumericExpression(core, 0, 1);\n    if (pValue.type == ValueTypeError) return pValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (core->machine->ioRegisters.attr.gamepadsEnabled == 0) return val_makeError(ErrorGamepadNotEnabled);\n        \n        int p = pValue.v.floatValue;\n        int active = 0;\n        int lastFrameActive = 0;\n        union Gamepad *gamepad = &core->machine->ioRegisters.gamepads[p];\n        union Gamepad *lastFrameGamepad = &core->interpreter->lastFrameGamepads[p];\n        switch (type)\n        {\n            case TokenUP:\n                active = gamepad->up;\n                lastFrameActive = lastFrameGamepad->up;\n                break;\n                \n            case TokenDOWN:\n                active = gamepad->down;\n                lastFrameActive = lastFrameGamepad->down;\n                break;\n\n            case TokenLEFT:\n                active = gamepad->left;\n                lastFrameActive = lastFrameGamepad->left;\n                break;\n\n            case TokenRIGHT:\n                active = gamepad->right;\n                lastFrameActive = lastFrameGamepad->right;\n                break;\n                \n            default:\n                assert(0);\n                break;\n        }\n        value.v.floatValue = active && !(tap && lastFrameActive) ? BAS_TRUE : BAS_FALSE;\n    }\n    return value;\n}\n\nstruct TypedValue fnc_BUTTON(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BUTTON\n    ++interpreter->pc;\n    \n    // TAP\n    bool tap = false;\n    if (interpreter->pc->type == TokenTAP)\n    {\n        ++interpreter->pc;\n        tap = true;\n    }\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // p expression\n    struct TypedValue pValue = itp_evaluateNumericExpression(core, 0, 1);\n    if (pValue.type == ValueTypeError) return pValue;\n    \n    int n = -1;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n    \n        // n expression\n        struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, 1);\n        if (nValue.type == ValueTypeError) return nValue;\n        \n        n = nValue.v.floatValue;\n    }\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (core->machine->ioRegisters.attr.gamepadsEnabled == 0) return val_makeError(ErrorGamepadNotEnabled);\n        \n        int p = pValue.v.floatValue;\n        union Gamepad *gamepad = &core->machine->ioRegisters.gamepads[p];\n\n        int active = (n == -1) ? (gamepad->buttonA || gamepad->buttonB) : (n == 0) ? gamepad->buttonA : gamepad->buttonB;\n        \n        if (active && tap)\n        {\n            // invalidate button if it was already pressed last frame\n            union Gamepad *lastFrameGamepad = &core->interpreter->lastFrameGamepads[p];\n            if ((n == -1) ? (lastFrameGamepad->buttonA || lastFrameGamepad->buttonB) : (n == 0) ? lastFrameGamepad->buttonA : lastFrameGamepad->buttonB)\n            {\n                active = 0;\n            }\n        }\n        \n        value.v.floatValue = active ? BAS_TRUE : BAS_FALSE;\n    }\n    return value;\n}\n\nstruct TypedValue fnc_TOUCH(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // TOUCH\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (core->machine->ioRegisters.attr.touchEnabled == 0) return val_makeError(ErrorTouchNotEnabled);\n        \n        value.v.floatValue = core->machine->ioRegisters.status.touch ? BAS_TRUE : BAS_FALSE;\n    }\n    return value;\n}\n\nstruct TypedValue fnc_TAP(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // TAP\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (core->machine->ioRegisters.attr.touchEnabled == 0) return val_makeError(ErrorTouchNotEnabled);\n        \n        value.v.floatValue = (core->machine->ioRegisters.status.touch && !core->interpreter->lastFrameIOStatus.touch) ? BAS_TRUE : BAS_FALSE;\n    }\n    return value;\n}\n\nstruct TypedValue fnc_TOUCH_X_Y(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // TOUCH.?\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (core->machine->ioRegisters.attr.touchEnabled == 0) return val_makeError(ErrorTouchNotEnabled);\n        \n        if (type == TokenTOUCHX)\n        {\n            value.v.floatValue = core->machine->ioRegisters.touchX;\n        }\n        else if (type == TokenTOUCHY)\n        {\n            value.v.floatValue = core->machine->ioRegisters.touchY;\n        }\n        else\n        {\n            assert(0);\n        }\n    }\n    return value;\n}\n\nstruct TypedValue fnc_PAUSE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // PAUSE\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (interpreter->handlesPause) return val_makeError(ErrorAutomaticPauseNotDisabled);\n        \n        value.v.floatValue = core->machine->ioRegisters.status.pause ? BAS_TRUE : BAS_FALSE;\n        core->machine->ioRegisters.status.pause = 0;\n    }\n    return value;\n}\n"
  },
  {
    "path": "core/interpreter/cmd_io.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_io_h\n#define cmd_io_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"value.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_KEYBOARD(struct Core *core);\nenum ErrorCode cmd_TOUCHSCREEN(struct Core *core);\nenum ErrorCode cmd_GAMEPAD(struct Core *core);\nenum ErrorCode cmd_PAUSE(struct Core *core);\nstruct TypedValue fnc_UP_DOWN_LEFT_RIGHT(struct Core *core);\nstruct TypedValue fnc_BUTTON(struct Core *core);\nstruct TypedValue fnc_TOUCH(struct Core *core);\nstruct TypedValue fnc_TAP(struct Core *core);\nstruct TypedValue fnc_TOUCH_X_Y(struct Core *core);\nstruct TypedValue fnc_PAUSE(struct Core *core);\n\n#endif /* cmd_io_h */\n"
  },
  {
    "path": "core/interpreter/cmd_maths.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#define _USE_MATH_DEFINES\n#include \"cmd_maths.h\"\n#include \"core.h\"\n#include <math.h>\n#include <assert.h>\n#include <stdlib.h>\n\nstruct TypedValue fnc_math0(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // function\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        switch (type)\n        {\n            case TokenPI:\n                value.v.floatValue = M_PI;\n                break;\n                \n            default:\n                assert(0);\n                break;\n        }\n    }\n    return value;\n}\n\nstruct TypedValue fnc_math1(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // function\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        switch (type)\n        {\n            case TokenABS:\n                value.v.floatValue = fabsf(xValue.v.floatValue);\n                break;\n                \n            case TokenACOS:\n                if (xValue.v.floatValue < -1.0 || xValue.v.floatValue > 1.0) return val_makeError(ErrorInvalidParameter);\n                value.v.floatValue = acosf(xValue.v.floatValue);\n                break;\n                \n            case TokenASIN:\n                if (xValue.v.floatValue < -1.0 || xValue.v.floatValue > 1.0) return val_makeError(ErrorInvalidParameter);\n                value.v.floatValue = asinf(xValue.v.floatValue);\n                break;\n                \n            case TokenATAN:\n                value.v.floatValue = atanf(xValue.v.floatValue);\n                break;\n                \n            case TokenCOS:\n                value.v.floatValue = cosf(xValue.v.floatValue);\n                break;\n                \n            case TokenEXP:\n                value.v.floatValue = expf(xValue.v.floatValue);\n                break;\n                \n            case TokenHCOS:\n                value.v.floatValue = coshf(xValue.v.floatValue);\n                break;\n                \n            case TokenHSIN:\n                value.v.floatValue = sinhf(xValue.v.floatValue);\n                break;\n                \n            case TokenHTAN:\n                value.v.floatValue = tanhf(xValue.v.floatValue);\n                break;\n                \n            case TokenINT:\n                value.v.floatValue = floorf(xValue.v.floatValue);\n                break;\n                \n            case TokenLOG:\n                if (xValue.v.floatValue <= 0) return val_makeError(ErrorInvalidParameter);\n                value.v.floatValue = logf(xValue.v.floatValue);\n                break;\n                \n            case TokenSGN:\n                value.v.floatValue = (xValue.v.floatValue > 0) ? 1 : (xValue.v.floatValue < 0) ? BAS_TRUE : BAS_FALSE;\n                break;\n                \n            case TokenSIN:\n                value.v.floatValue = sinf(xValue.v.floatValue);\n                break;\n                \n            case TokenSQR:\n                if (xValue.v.floatValue < 0) return val_makeError(ErrorInvalidParameter);\n                value.v.floatValue = sqrtf(xValue.v.floatValue);\n                break;\n                \n            case TokenTAN:\n                value.v.floatValue = tanf(xValue.v.floatValue);\n                break;\n                                \n            default:\n                assert(0);\n                break;\n        }\n    }\n    return value;\n}\n\nstruct TypedValue fnc_math2(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // function\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n        ++interpreter->pc;\n    \n    // x expression\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n\n    // y expression\n    struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (yValue.type == ValueTypeError) return yValue;\n\n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        float x = xValue.v.floatValue;\n        float y = yValue.v.floatValue;\n        \n        switch (type)\n        {\n            case TokenMAX:\n                value.v.floatValue = (x > y) ? x : y;\n                break;\n                \n            case TokenMIN:\n                value.v.floatValue = (x < y) ? x : y;\n                break;\n                \n            default:\n                assert(0);\n                break;\n        }\n    }\n    return value;\n}\n\nenum ErrorCode cmd_RANDOMIZE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // RANDOMIZE\n    ++interpreter->pc;\n    \n    // value\n    struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric);\n    if (value.type == ValueTypeError) return value.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->seed = value.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_RND(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // RND\n    ++interpreter->pc;\n    \n    int x = -1;\n    if (interpreter->pc->type == TokenBracketOpen)\n    {\n        // bracket open\n        ++interpreter->pc;\n    \n        // expression\n        struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (xValue.type == ValueTypeError) return xValue;\n        x = xValue.v.floatValue;\n        \n        // bracket close\n        if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n        ++interpreter->pc;\n    }\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int seed = (1140671485 * interpreter->seed + 12820163) & 0xFFFFFF;\n        interpreter->seed = seed;\n        float rnd = seed / (float)0x1000000;\n        \n        if (x >= 0)\n        {\n            // integer 0...x\n            value.v.floatValue = floorf(rnd * (x + 1));\n        }\n        else\n        {\n            // float 0..<1\n            value.v.floatValue = rnd;\n        }\n    }\n    return value;\n}\n\nenum ErrorCode cmd_ADD(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // ADD\n    ++interpreter->pc;\n    \n    enum ErrorCode errorCode = ErrorNone;\n    \n    // Variable\n    enum ValueType valueType = ValueTypeNull;\n    union Value *varValue = itp_readVariable(core, &valueType, &errorCode, false);\n    if (!varValue) return errorCode;\n    if (valueType != ValueTypeFloat) return ErrorTypeMismatch;\n    \n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // n vale\n    struct TypedValue nValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    bool hasRange = false;\n    float base = 0;\n    float top = 0;\n    \n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // base value\n        struct TypedValue baseValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (baseValue.type == ValueTypeError) return baseValue.v.errorCode;\n        base = baseValue.v.floatValue;\n        \n        // TO\n        if (interpreter->pc->type != TokenTO) return ErrorSyntax;\n        ++interpreter->pc;\n        \n        // top value\n        struct TypedValue topValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (topValue.type == ValueTypeError) return topValue.v.errorCode;\n        top = topValue.v.floatValue;\n        \n        hasRange = true;\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        varValue->floatValue += nValue.v.floatValue;\n        if (hasRange)\n        {\n            if (varValue->floatValue < base) varValue->floatValue = top;\n            if (varValue->floatValue > top) varValue->floatValue = base;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_INC_DEC(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // INC/DEC\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    enum ErrorCode errorCode = ErrorNone;\n    \n    // Variable\n    enum ValueType valueType = ValueTypeNull;\n    union Value *varValue = itp_readVariable(core, &valueType, &errorCode, false);\n    if (!varValue) return errorCode;\n    if (valueType != ValueTypeFloat) return ErrorTypeMismatch;\n    \n    if (interpreter->pass == PassRun)\n    {\n        switch (type)\n        {\n            case TokenINC:\n                ++varValue->floatValue;\n                break;\n                \n            case TokenDEC:\n                --varValue->floatValue;\n                break;\n                \n            default:\n                assert(0);\n                break;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n"
  },
  {
    "path": "core/interpreter/cmd_maths.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_maths_h\n#define cmd_maths_h\n\n#include <stdio.h>\n#include \"value.h\"\n\nstruct Core;\n\nstruct TypedValue fnc_math0(struct Core *core);\nstruct TypedValue fnc_math1(struct Core *core);\nstruct TypedValue fnc_math2(struct Core *core);\nenum ErrorCode cmd_RANDOMIZE(struct Core *core);\nstruct TypedValue fnc_RND(struct Core *core);\nenum ErrorCode cmd_ADD(struct Core *core);\nenum ErrorCode cmd_INC_DEC(struct Core *core);\n\n#endif /* cmd_maths_h */\n"
  },
  {
    "path": "core/interpreter/cmd_memory.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_memory.h\"\n#include \"core.h\"\n#include \"data_manager.h\"\n#include <assert.h>\n\nstruct TypedValue fnc_PEEK(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // PEEK/W/L\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (addressValue.type == ValueTypeError) return addressValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        switch (type)\n        {\n            case TokenPEEK:\n            {\n                int peek = machine_peek(core, addressValue.v.floatValue);\n                if (peek == -1) return val_makeError(ErrorIllegalMemoryAccess);\n                resultValue.v.floatValue = peek;\n                break;\n            }\n            \n            case TokenPEEKW:\n            {\n                int peek1 = machine_peek(core, addressValue.v.floatValue);\n                int peek2 = machine_peek(core, addressValue.v.floatValue + 1);\n                if (peek1 == -1 || peek2 == -1) return val_makeError(ErrorIllegalMemoryAccess);\n                \n                int16_t value = peek1 | (peek2 << 8);\n                resultValue.v.floatValue = value;\n                break;\n            }\n            \n            case TokenPEEKL:\n            {\n                int peek1 = machine_peek(core, addressValue.v.floatValue);\n                int peek2 = machine_peek(core, addressValue.v.floatValue + 1);\n                int peek3 = machine_peek(core, addressValue.v.floatValue + 2);\n                int peek4 = machine_peek(core, addressValue.v.floatValue + 3);\n                if (peek1 == -1 || peek2 == -1 || peek3 == -1 || peek4 == -1) return val_makeError(ErrorIllegalMemoryAccess);\n                \n                int32_t value = peek1 | (peek2 << 8) | (peek3 << 16) | (peek4 << 24);\n                resultValue.v.floatValue = value;\n                break;\n            }\n            \n            default:\n                assert(0);\n        }\n    }\n    return resultValue;\n}\n\nenum ErrorCode cmd_POKE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // POKE/W/L\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // address value\n    struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (addressValue.type == ValueTypeError) return addressValue.v.errorCode;\n    \n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // poke vale\n    struct TypedValue pokeValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (pokeValue.type == ValueTypeError) return pokeValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        switch (type)\n        {\n            case TokenPOKE:\n            {\n                bool poke = machine_poke(core, addressValue.v.floatValue, pokeValue.v.floatValue);\n                if (!poke) return ErrorIllegalMemoryAccess;\n                break;\n            }\n            \n            case TokenPOKEW:\n            {\n                int16_t value = pokeValue.v.floatValue;\n                bool poke1 = machine_poke(core, addressValue.v.floatValue    , value);\n                bool poke2 = machine_poke(core, addressValue.v.floatValue + 1, value >> 8);\n                if (!poke1 || !poke2) return ErrorIllegalMemoryAccess;\n                break;\n            }\n            \n            case TokenPOKEL:\n            {\n                int32_t value = pokeValue.v.floatValue;\n                bool poke1 = machine_poke(core, addressValue.v.floatValue    , value);\n                bool poke2 = machine_poke(core, addressValue.v.floatValue + 1, value >> 8);\n                bool poke3 = machine_poke(core, addressValue.v.floatValue + 2, value >> 16);\n                bool poke4 = machine_poke(core, addressValue.v.floatValue + 3, value >> 24);\n                if (!poke1 || !poke2 || !poke3 || !poke4) return ErrorIllegalMemoryAccess;\n                break;\n            }\n                \n            default:\n                assert(0);\n        }\n\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_FILL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // FILL\n    ++interpreter->pc;\n\n    // start value\n    struct TypedValue startValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (startValue.type == ValueTypeError) return startValue.v.errorCode;\n\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n\n    // length value\n    struct TypedValue lengthValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (lengthValue.type == ValueTypeError) return lengthValue.v.errorCode;\n    \n    int fill = 0;\n    if (interpreter->pc->type == TokenComma)\n    {\n        ++interpreter->pc;\n    \n        // fill value\n        struct TypedValue fillValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (fillValue.type == ValueTypeError) return fillValue.v.errorCode;\n        fill = fillValue.v.floatValue;\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        int start = startValue.v.floatValue;\n        int length = lengthValue.v.floatValue;\n        for (int i = 0; i < length; i++)\n        {\n            bool poke = machine_poke(core, start + i, fill);\n            if (!poke) return ErrorIllegalMemoryAccess;\n        }\n        interpreter->cycles += length;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_COPY(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // COPY\n    ++interpreter->pc;\n    \n    // source value\n    struct TypedValue sourceValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (sourceValue.type == ValueTypeError) return sourceValue.v.errorCode;\n    \n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // length value\n    struct TypedValue lengthValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (lengthValue.type == ValueTypeError) return lengthValue.v.errorCode;\n\n    if (interpreter->pc->type != TokenTO) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // destination value\n    struct TypedValue destinationValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (destinationValue.type == ValueTypeError) return destinationValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int source = sourceValue.v.floatValue;\n        int length = lengthValue.v.floatValue;\n        int destination = destinationValue.v.floatValue;\n        if (source < destination)\n        {\n            for (int i = length - 1; i >= 0; i--)\n            {\n                int peek = machine_peek(core, source + i);\n                if (peek == -1) return ErrorIllegalMemoryAccess;\n                bool poke = machine_poke(core, destination + i, peek);\n                if (!poke) return ErrorIllegalMemoryAccess;\n            }\n        }\n        else if (source > destination)\n        {\n            for (int i = 0; i < length; i++)\n            {\n                int peek = machine_peek(core, source + i);\n                if (peek == -1) return ErrorIllegalMemoryAccess;\n                bool poke = machine_poke(core, destination + i, peek);\n                if (!poke) return ErrorIllegalMemoryAccess;\n            }\n        }\n        interpreter->cycles += length;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_ROM_SIZE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // ROM/SIZE\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // index expression\n    struct TypedValue indexValue = itp_evaluateNumericExpression(core, 0, MAX_ENTRIES - 1);\n    if (indexValue.type == ValueTypeError) return indexValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int index = indexValue.v.floatValue;\n        if (type == TokenSIZE)\n        {\n            value.v.floatValue = interpreter->romDataManager.entries[index].length;\n        }\n        else\n        {\n            value.v.floatValue = interpreter->romDataManager.entries[index].start;\n        }\n    }\n    return value;\n}\n\nenum ErrorCode cmd_ROL_ROR(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // ROL/ROR\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // address value\n    struct TypedValue addressValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (addressValue.type == ValueTypeError) return addressValue.v.errorCode;\n    \n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // n vale\n    struct TypedValue nValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int value = machine_peek(core, addressValue.v.floatValue);\n        if (value == -1) return ErrorIllegalMemoryAccess;\n        \n        int n = (int)nValue.v.floatValue;\n        if (type == TokenROR)\n        {\n            n = -n;\n        }\n        n &= 0x07;\n        \n        value = value << n;\n        value = value | (value >> 8);\n        \n        bool poke = machine_poke(core, addressValue.v.floatValue, value);\n        if (!poke) return ErrorIllegalMemoryAccess;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n"
  },
  {
    "path": "core/interpreter/cmd_memory.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_memory_h\n#define cmd_memory_h\n\n#include <stdio.h>\n#include \"error.h\"\n\nstruct Core;\n\nstruct TypedValue fnc_PEEK(struct Core *core);\nenum ErrorCode cmd_POKE(struct Core *core);\nenum ErrorCode cmd_FILL(struct Core *core);\nenum ErrorCode cmd_COPY(struct Core *core);\nstruct TypedValue fnc_ROM_SIZE(struct Core *core);\nenum ErrorCode cmd_ROL_ROR(struct Core *core);\n\n#endif /* cmd_memory_h */\n"
  },
  {
    "path": "core/interpreter/cmd_screen.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_screen.h\"\n#include \"core.h\"\n#include <assert.h>\n#include <stdint.h>\n#include \"interpreter_utils.h\"\n\nenum ErrorCode cmd_PALETTE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // PALETTE\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_PALETTES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // c0 value\n    struct TypedValue c0Value = itp_evaluateOptionalNumericExpression(core, 0, 63);\n    if (c0Value.type == ValueTypeError) return c0Value.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // c1 value\n    struct TypedValue c1Value = itp_evaluateOptionalNumericExpression(core, 0, 63);\n    if (c1Value.type == ValueTypeError) return c1Value.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // c2 value\n    struct TypedValue c2Value = itp_evaluateOptionalNumericExpression(core, 0, 63);\n    if (c2Value.type == ValueTypeError) return c2Value.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // c3 value\n    struct TypedValue c3Value = itp_evaluateOptionalNumericExpression(core, 0, 63);\n    if (c3Value.type == ValueTypeError) return c3Value.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        uint8_t *palColors = &core->machine->colorRegisters.colors[n * 4];\n        if (c0Value.type != ValueTypeNull) palColors[0] = c0Value.v.floatValue;\n        if (c1Value.type != ValueTypeNull) palColors[1] = c1Value.v.floatValue;\n        if (c2Value.type != ValueTypeNull) palColors[2] = c2Value.v.floatValue;\n        if (c3Value.type != ValueTypeNull) palColors[3] = c3Value.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_SCROLL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SCROLL\n    ++interpreter->pc;\n    \n    // bg value\n    struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1);\n    if (bgValue.type == ValueTypeError) return bgValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n\n    if (interpreter->pass == PassRun)\n    {\n        struct VideoRegisters *reg = &core->machine->videoRegisters;\n        int bg = bgValue.v.floatValue;\n        int x = (int)xValue.v.floatValue;\n        int y = (int)yValue.v.floatValue;\n        if (bg == 0)\n        {\n            reg->scrollAX = x & 0xFF;\n            reg->scrollAY = y & 0xFF;\n            reg->scrollMSB.aX = (x >> 8) & 1;\n            reg->scrollMSB.aY = (y >> 8) & 1;\n        }\n        else\n        {\n            reg->scrollBX = x & 0xFF;\n            reg->scrollBY = y & 0xFF;\n            reg->scrollMSB.bX = (x >> 8) & 1;\n            reg->scrollMSB.bY = (y >> 8) & 1;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_DISPLAY(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // DISPLAY\n    ++interpreter->pc;\n    \n    // obsolete syntax!\n    \n    // atrb value\n    struct TypedValue aValue = itp_evaluateDisplayAttributes(core, core->machine->videoRegisters.attr);\n    if (aValue.type == ValueTypeError) return aValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n         core->machine->videoRegisters.attr.value = aValue.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_SPRITE_VIEW(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SPRITE VIEW\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    // ON/OFF\n    enum TokenType type = interpreter->pc->type;\n    if (type != TokenON && type != TokenOFF) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassRun)\n    {\n        core->machine->videoRegisters.attr.spritesEnabled = type == TokenON ? 1 : 0;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_BG_VIEW(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BG VIEW\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    // ON/OFF\n    enum TokenType type = interpreter->pc->type;\n    if (type != TokenON && type != TokenOFF) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // bg value\n    struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1);\n    if (bgValue.type == ValueTypeError) return bgValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int value = type == TokenON ? 1 : 0;\n        if (bgValue.v.floatValue == 0)\n        {\n            core->machine->videoRegisters.attr.planeAEnabled = value;\n        }\n        else\n        {\n            core->machine->videoRegisters.attr.planeBEnabled = value;\n        }\n    }\n\n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_CELL_SIZE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // CELL SIZE\n    ++interpreter->pc;\n    ++interpreter->pc;\n    \n    // bg value\n    struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1);\n    if (bgValue.type == ValueTypeError) return bgValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // size value\n    struct TypedValue sValue = itp_evaluateOptionalNumericExpression(core, 0, 1);\n    if (sValue.type == ValueTypeError) return sValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (bgValue.v.floatValue == 0)\n        {\n            core->machine->videoRegisters.attr.planeACellSize = sValue.v.floatValue;\n        }\n        else\n        {\n            core->machine->videoRegisters.attr.planeBCellSize = sValue.v.floatValue;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_COLOR(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // COLOR\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // pal expression\n    struct TypedValue pValue = itp_evaluateNumericExpression(core, 0, NUM_PALETTES - 1);\n    if (pValue.type == ValueTypeError) return pValue;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n\n    // pal expression\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, 3);\n    if (nValue.type == ValueTypeError) return nValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int p = pValue.v.floatValue;\n        int n = nValue.v.floatValue;\n        value.v.floatValue = core->machine->colorRegisters.colors[p * 4 + n];\n    }\n    return value;\n}\n\nstruct TypedValue fnc_screen0(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // function\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        switch (type)\n        {\n            case TokenTIMER:\n                value.v.floatValue = core->interpreter->timer;\n                break;\n                \n            case TokenRASTER:\n                value.v.floatValue = core->machine->videoRegisters.rasterLine;\n                break;\n                \n            case TokenDISPLAY:\n                // obsolete syntax!\n                value.v.floatValue = core->machine->videoRegisters.attr.value;\n                break;\n                \n            default:\n                assert(0);\n                break;\n        }\n    }\n    return value;\n}\n\nstruct TypedValue fnc_SCROLL_X_Y(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SCROLL.?\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // bg value\n    struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1);\n    if (bgValue.type == ValueTypeError) return bgValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int bg = bgValue.v.floatValue;\n        struct VideoRegisters *reg = &core->machine->videoRegisters;\n        switch (type)\n        {\n            case TokenSCROLLX:\n                if (bg == 0)\n                {\n                    value.v.floatValue = reg->scrollAX | (reg->scrollMSB.aX << 8);\n                }\n                else\n                {\n                    value.v.floatValue = reg->scrollBX | (reg->scrollMSB.bX << 8);\n                }\n                break;\n                \n            case TokenSCROLLY:\n                if (bg == 0)\n                {\n                    value.v.floatValue = reg->scrollAY | (reg->scrollMSB.aY << 8);\n                }\n                else\n                {\n                    value.v.floatValue = reg->scrollBY | (reg->scrollMSB.bY << 8);\n                }\n                break;\n                \n            default:\n                assert(0);\n                break;\n        }\n    }\n    return value;\n}\n"
  },
  {
    "path": "core/interpreter/cmd_screen.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_screen_h\n#define cmd_screen_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"value.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_PALETTE(struct Core *core);\nenum ErrorCode cmd_SCROLL(struct Core *core);\nenum ErrorCode cmd_DISPLAY(struct Core *core);\nenum ErrorCode cmd_SPRITE_VIEW(struct Core *core);\nenum ErrorCode cmd_BG_VIEW(struct Core *core);\nenum ErrorCode cmd_CELL_SIZE(struct Core *core);\nstruct TypedValue fnc_COLOR(struct Core *core);\nstruct TypedValue fnc_screen0(struct Core *core);\nstruct TypedValue fnc_SCROLL_X_Y(struct Core *core);\n\n#endif /* cmd_screen_h */\n"
  },
  {
    "path": "core/interpreter/cmd_sprites.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_sprites.h\"\n#include \"core.h\"\n#include \"value.h\"\n#include \"cmd_text.h\"\n#include \"interpreter_utils.h\"\n#include \"sprites_lib.h\"\n#include <assert.h>\n\nenum ErrorCode cmd_SPRITE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SPRITE\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type == TokenComma)\n    {\n        ++interpreter->pc;\n        \n        // x value\n        struct TypedValue xValue = itp_evaluateOptionalExpression(core, TypeClassNumeric);\n        if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n        \n        // comma\n        if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n        ++interpreter->pc;\n        \n        // y value\n        struct TypedValue yValue = itp_evaluateOptionalExpression(core, TypeClassNumeric);\n        if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n\n        // comma\n        if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n        ++interpreter->pc;\n        \n        // c value\n        struct TypedValue cValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_CHARACTERS - 1);\n        if (cValue.type == ValueTypeError) return cValue.v.errorCode;\n\n        if (interpreter->pass == PassRun)\n        {\n            int n = nValue.v.floatValue;\n            struct Sprite *sprite = &core->machine->spriteRegisters.sprites[n];\n            if (xValue.type != ValueTypeNull) sprite->x = ((int)xValue.v.floatValue + SPRITE_OFFSET_X) & 0xFF;\n            if (yValue.type != ValueTypeNull) sprite->y = ((int)yValue.v.floatValue + SPRITE_OFFSET_Y) & 0xFF;\n            if (cValue.type != ValueTypeNull) sprite->character = cValue.v.floatValue;\n        }\n    }\n    else\n    {\n        struct SimpleAttributes attrs;\n        enum ErrorCode attrsError = itp_evaluateSimpleAttributes(core, &attrs);\n        if (attrsError != ErrorNone) return attrsError;\n        \n        if (interpreter->pass == PassRun)\n        {\n            int n = nValue.v.floatValue;\n            struct Sprite *sprite = &core->machine->spriteRegisters.sprites[n];\n            \n            if (attrs.pal >= 0) sprite->attr.palette = attrs.pal;\n            if (attrs.flipX >= 0) sprite->attr.flipX = attrs.flipX;\n            if (attrs.flipY >= 0) sprite->attr.flipY = attrs.flipY;\n            if (attrs.prio >= 0) sprite->attr.priority = attrs.prio;\n            if (attrs.size >= 0) sprite->attr.size = attrs.size;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_SPRITE_A(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SPRITE.A\n    ++interpreter->pc;\n    \n    // n value\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1);\n    if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n    \n    struct Sprite *sprite = NULL;\n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        sprite = &core->machine->spriteRegisters.sprites[n];\n    }\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    union CharacterAttributes attr;\n    if (sprite)\n    {\n        attr = sprite->attr;\n    }\n    else\n    {\n        attr.value = 0;\n    }\n    \n    // attr value\n    struct TypedValue aValue = itp_evaluateCharAttributes(core, attr);\n    if (aValue.type == ValueTypeError) return aValue.v.errorCode;\n\n    if (interpreter->pass == PassRun)\n    {\n        sprite->attr.value = aValue.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_SPRITE_OFF(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SPRITE\n    ++interpreter->pc;\n    \n    // OFF\n    if (interpreter->pc->type != TokenOFF) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    int from = 0;\n    int to = NUM_SPRITES - 1;\n    \n    if (!itp_isEndOfCommand(interpreter))\n    {\n        // from value\n        struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1);\n        if (nValue.type == ValueTypeError) return nValue.v.errorCode;\n        from = nValue.v.floatValue;\n        to = from;\n        \n        // TO\n        if (interpreter->pc->type == TokenTO)\n        {\n            ++interpreter->pc;\n        \n            // to value\n            struct TypedValue mValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1);\n            if (mValue.type == ValueTypeError) return mValue.v.errorCode;\n            to = mValue.v.floatValue;\n        }\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        for (int i = from; i <= to; i++)\n        {\n            struct Sprite *sprite = &core->machine->spriteRegisters.sprites[i];\n            sprite->x = 0;\n            sprite->y = 0;\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_SPRITE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SPRITE.?\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1);\n    if (nValue.type == ValueTypeError) return nValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int n = nValue.v.floatValue;\n        struct Sprite *sprite = &core->machine->spriteRegisters.sprites[n];\n        switch (type)\n        {\n            case TokenSPRITEX:\n                value.v.floatValue = sprite->x - SPRITE_OFFSET_X;\n                break;\n                \n            case TokenSPRITEY:\n                value.v.floatValue = sprite->y - SPRITE_OFFSET_Y;\n                break;\n                \n            case TokenSPRITEC:\n                value.v.floatValue = sprite->character;\n                break;\n                \n            case TokenSPRITEA:\n                value.v.floatValue = sprite->attr.value;\n                break;\n\n            default:\n                assert(0);\n                break;\n        }\n    }\n    return value;\n}\n\nstruct TypedValue fnc_SPRITE_HIT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SPRITE\n    ++interpreter->pc;\n    \n    // HIT\n    if (interpreter->pc->type != TokenHIT) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // sprite number\n    struct TypedValue nValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1);\n    if (nValue.type == ValueTypeError) return nValue;\n    \n    int first = 0;\n    int last = NUM_SPRITES - 1;\n    \n    // other sprite number\n    if (interpreter->pc->type == TokenComma)\n    {\n        ++interpreter->pc;\n        struct TypedValue otherValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1);\n        if (otherValue.type == ValueTypeError) return otherValue;\n        first = otherValue.v.floatValue;\n        last = first;\n        \n        // last sprite number\n        if (interpreter->pc->type == TokenTO)\n        {\n            ++interpreter->pc;\n            struct TypedValue lastValue = itp_evaluateNumericExpression(core, 0, NUM_SPRITES - 1);\n            if (lastValue.type == ValueTypeError) return lastValue;\n            last = lastValue.v.floatValue;\n        }\n    }\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        bool hits = sprlib_checkCollision(&interpreter->spritesLib, nValue.v.floatValue, first, last);\n        value.v.floatValue = hits ? BAS_TRUE : BAS_FALSE;\n    }\n    \n    return value;\n}\n\nstruct TypedValue fnc_HIT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // HIT\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        value.v.floatValue = interpreter->spritesLib.lastHit;\n    }\n    \n    return value;\n}\n"
  },
  {
    "path": "core/interpreter/cmd_sprites.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_sprites_h\n#define cmd_sprites_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"value.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_SPRITE(struct Core *core);\nenum ErrorCode cmd_SPRITE_A(struct Core *core);\nenum ErrorCode cmd_SPRITE_OFF(struct Core *core);\nstruct TypedValue fnc_SPRITE(struct Core *core);\nstruct TypedValue fnc_SPRITE_HIT(struct Core *core);\nstruct TypedValue fnc_HIT(struct Core *core);\n\n#endif /* cmd_sprites_h */\n"
  },
  {
    "path": "core/interpreter/cmd_strings.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_strings.h\"\n#include \"core.h\"\n#include <string.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <assert.h>\n#include \"rcstring.h\"\n\nstruct TypedValue fnc_ASC(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // ASC\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString);\n    if (stringValue.type == ValueTypeError) return stringValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        char ch = stringValue.v.stringValue->chars[0];\n        rcstring_release(stringValue.v.stringValue);\n        \n        if (ch == 0) return val_makeError(ErrorInvalidParameter);\n        value.v.floatValue = ch;\n    }\n    return value;\n}\n\nstruct TypedValue fnc_BIN_HEX(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // BIN$/HEX$\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // x expression\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue;\n    \n    int maxLen = (type == TokenHEX) ? 8 : 16;\n    int width = 0;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // width expression\n        struct TypedValue widthValue = itp_evaluateNumericExpression(core, 0, maxLen);\n        if (widthValue.type == ValueTypeError) return widthValue;\n        width = widthValue.v.floatValue;\n    }\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeString;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int x = xValue.v.floatValue;\n        \n        struct RCString *rcstring = rcstring_new(NULL, maxLen);\n        if (!rcstring) return val_makeError(ErrorOutOfMemory);\n        \n        if (type == TokenBIN)\n        {\n            txtlib_itobin(rcstring->chars, maxLen + 1, width, x);\n        }\n        else if (type == TokenHEX)\n        {\n            snprintf(rcstring->chars, maxLen + 1, \"%0*X\", width, x);\n        }\n        resultValue.v.stringValue = rcstring;\n        interpreter->cycles += maxLen;\n    }\n    return resultValue;\n}\n\nstruct TypedValue fnc_CHR(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // CHR$\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue numericValue = itp_evaluateNumericExpression(core, 0, 255);\n    if (numericValue.type == ValueTypeError) return numericValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeString;\n    \n    if (interpreter->pass == PassRun)\n    {\n        char ch = numericValue.v.floatValue;\n        struct RCString *rcstring = rcstring_new(&ch, 1);\n        if (!rcstring) return val_makeError(ErrorOutOfMemory);\n        \n        resultValue.v.stringValue = rcstring;\n        interpreter->cycles += 1;\n    }\n    return resultValue;\n}\n\nstruct TypedValue fnc_INKEY(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // INKEY$\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeString;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (!core->machine->ioRegisters.attr.keyboardEnabled) return val_makeError(ErrorKeyboardNotEnabled);\n        \n        char key = core->machine->ioRegisters.key;\n        if (key)\n        {\n            core->machine->ioRegisters.key = 0;\n            \n            struct RCString *rcstring = rcstring_new(&key, 1);\n            if (!rcstring) return val_makeError(ErrorOutOfMemory);\n            \n            resultValue.v.stringValue = rcstring;\n            interpreter->cycles += 1;\n        }\n        else\n        {\n            resultValue.v.stringValue = interpreter->nullString;\n            rcstring_retain(resultValue.v.stringValue);\n        }\n    }\n    return resultValue;\n}\n\nstruct TypedValue fnc_INSTR(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // INSTR\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // string expression\n    struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString);\n    if (stringValue.type == ValueTypeError) return stringValue;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // search value\n    struct TypedValue searchValue = itp_evaluateExpression(core, TypeClassString);\n    if (searchValue.type == ValueTypeError) return searchValue;\n    \n    int startIndex = 0;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // number value\n        struct TypedValue posValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (posValue.type == ValueTypeError) return posValue;\n        \n        startIndex = posValue.v.floatValue - 1;\n    }\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        char *string = stringValue.v.stringValue->chars;\n        char *search = searchValue.v.stringValue->chars;\n        size_t stringlen = strlen(string);\n        if (startIndex >= stringlen || search[0] == 0)\n        {\n            resultValue.v.floatValue = 0;\n        }\n        else\n        {\n            char *found = strstr(&string[startIndex], search);\n            if (found)\n            {\n                resultValue.v.floatValue = (found - string) + 1;\n            }\n            else\n            {\n                resultValue.v.floatValue = 0;\n            }\n        }\n        rcstring_release(stringValue.v.stringValue);\n        rcstring_release(searchValue.v.stringValue);\n    }\n    return resultValue;\n}\n\nstruct TypedValue fnc_LEFTStr_RIGHTStr(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // LEFT$/RIGHT$\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString);\n    if (stringValue.type == ValueTypeError) return stringValue;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (numberValue.type == ValueTypeError) return numberValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeString;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (numberValue.v.floatValue < 0) return val_makeError(ErrorInvalidParameter);\n        \n        size_t len = strlen(stringValue.v.stringValue->chars);\n        size_t number = numberValue.v.floatValue;\n        \n        if (number < len)\n        {\n            size_t start = (type == TokenLEFTStr) ? 0 : len - number;\n            \n            struct RCString *rcstring = rcstring_new(&stringValue.v.stringValue->chars[start], number);\n            if (!rcstring) return val_makeError(ErrorOutOfMemory);\n            \n            resultValue.v.stringValue = rcstring;\n            interpreter->cycles += number;\n        }\n        else\n        {\n            resultValue.v.stringValue = stringValue.v.stringValue;\n            rcstring_retain(resultValue.v.stringValue);\n        }\n        rcstring_release(stringValue.v.stringValue);\n    }\n    return resultValue;\n}\n\nstruct TypedValue fnc_LEN(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // LEN\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString);\n    if (stringValue.type == ValueTypeError) return stringValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        value.v.floatValue = strlen(stringValue.v.stringValue->chars);\n        rcstring_release(stringValue.v.stringValue);\n    }\n    return value;\n}\n\nstruct TypedValue fnc_MID(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // MID$\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // string expression\n    struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString);\n    if (stringValue.type == ValueTypeError) return stringValue;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // position value\n    struct TypedValue posValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (posValue.type == ValueTypeError) return posValue;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // number value\n    struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (numberValue.type == ValueTypeError) return numberValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeString;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (numberValue.v.floatValue < 0) return val_makeError(ErrorInvalidParameter);\n        if (posValue.v.floatValue < 1) return val_makeError(ErrorInvalidParameter);\n        \n        size_t len = strlen(stringValue.v.stringValue->chars);\n        size_t index = posValue.v.floatValue - 1;\n        size_t number = numberValue.v.floatValue;\n        \n        if (index >= len)\n        {\n            resultValue.v.stringValue = interpreter->nullString;\n            rcstring_retain(resultValue.v.stringValue);\n        }\n        else if (index > 0 || number < len)\n        {\n            if (index + number > len)\n            {\n                number = len - index;\n            }\n            struct RCString *rcstring = rcstring_new(&stringValue.v.stringValue->chars[index], number);\n            if (!rcstring) return val_makeError(ErrorOutOfMemory);\n            \n            resultValue.v.stringValue = rcstring;\n            interpreter->cycles += number;\n        }\n        else\n        {\n            resultValue.v.stringValue = stringValue.v.stringValue;\n            rcstring_retain(resultValue.v.stringValue);\n        }\n        \n        rcstring_release(stringValue.v.stringValue);\n    }\n    return resultValue;\n}\n\nstruct TypedValue fnc_STR(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // STR$\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue numericValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (numericValue.type == ValueTypeError) return numericValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue resultValue;\n    resultValue.type = ValueTypeString;\n    \n    if (interpreter->pass == PassRun)\n    {\n        struct RCString *rcstring = rcstring_new(NULL, 20);\n        if (!rcstring) return val_makeError(ErrorOutOfMemory);\n        \n        snprintf(rcstring->chars, 20, \"%0.7g\", numericValue.v.floatValue);\n        resultValue.v.stringValue = rcstring;\n        interpreter->cycles += strlen(rcstring->chars);\n    }\n    return resultValue;\n}\n\nstruct TypedValue fnc_VAL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // VAL\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // expression\n    struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString);\n    if (stringValue.type == ValueTypeError) return stringValue;\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        value.v.floatValue = atof(stringValue.v.stringValue->chars);\n        rcstring_release(stringValue.v.stringValue);\n    }\n    return value;\n}\n\nenum ErrorCode cmd_LEFT_RIGHT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // LEFT$/RIGHT$\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // variable\n    enum ErrorCode errorCode = ErrorNone;\n    enum ValueType valueType = ValueTypeNull;\n    union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true);\n    if (!varValue) return errorCode;\n    if (valueType != ValueTypeString) return ErrorTypeMismatch;\n    \n    size_t number = SIZE_MAX;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // number expression\n        struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (numberValue.type == ValueTypeError) return numberValue.v.errorCode;\n        number = numberValue.v.floatValue;\n    }\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // equal sign\n    if (interpreter->pc->type != TokenEq) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // replace expression\n    struct TypedValue replaceValue = itp_evaluateExpression(core, TypeClassString);\n    if (replaceValue.type == ValueTypeError) return replaceValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        size_t resultLen = strlen(varValue->stringValue->chars);\n        \n        struct RCString *resultRCString = varValue->stringValue;\n        if (resultRCString->refCount > 1)\n        {\n            // copy string if shared\n            resultRCString = rcstring_new(varValue->stringValue->chars, resultLen);\n            rcstring_release(varValue->stringValue);\n            varValue->stringValue = resultRCString;\n        }\n        \n        char *resultString = resultRCString->chars;\n        char *replaceString = replaceValue.v.stringValue->chars;\n        size_t replaceLen = strlen(replaceString);\n        if (number > replaceLen)\n        {\n            number = replaceLen;\n        }\n        if (number > resultLen)\n        {\n            number = resultLen;\n        }\n        \n        if (type == TokenLEFTStr)\n        {\n            for (size_t i = 0; i < number; i++)\n            {\n                resultString[i] = replaceString[i];\n            }\n        }\n        else if (type == TokenRIGHTStr)\n        {\n            for (size_t i = 0; i < number; i++)\n            {\n                resultString[resultLen - 1 - i] = replaceString[replaceLen - 1 - i];\n            }\n        }\n        interpreter->cycles += number;\n        \n        rcstring_release(replaceValue.v.stringValue);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_MID(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // MID$\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // variable\n    enum ErrorCode errorCode = ErrorNone;\n    enum ValueType valueType = ValueTypeNull;\n    union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true);\n    if (!varValue) return errorCode;\n    if (valueType != ValueTypeString) return ErrorTypeMismatch;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // position expression\n    struct TypedValue posValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (posValue.type == ValueTypeError) return posValue.v.errorCode;\n    \n    size_t number = SIZE_MAX;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // number expression\n        struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric);\n        if (numberValue.type == ValueTypeError) return numberValue.v.errorCode;\n        number = numberValue.v.floatValue;\n    }\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // equal sign\n    if (interpreter->pc->type != TokenEq) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // replace expression\n    struct TypedValue replaceValue = itp_evaluateExpression(core, TypeClassString);\n    if (replaceValue.type == ValueTypeError) return replaceValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        size_t index = posValue.v.floatValue - 1;\n        size_t resultLen = strlen(varValue->stringValue->chars);\n        \n        struct RCString *resultRCString = varValue->stringValue;\n        if (resultRCString->refCount > 1)\n        {\n            // copy string if shared\n            resultRCString = rcstring_new(varValue->stringValue->chars, resultLen);\n            rcstring_release(varValue->stringValue);\n            varValue->stringValue = resultRCString;\n        }\n        \n        if (index < resultLen)\n        {\n            char *resultString = resultRCString->chars;\n            char *replaceString = replaceValue.v.stringValue->chars;\n            size_t replaceLen = strlen(replaceString);\n            if (number > replaceLen)\n            {\n                number = replaceLen;\n            }\n            if (index + number > resultLen)\n            {\n                number = resultLen - index;\n            }\n            \n            for (size_t i = 0; i < number; i++)\n            {\n                resultString[index + i] = replaceString[i];\n            }\n            interpreter->cycles += number;\n        }\n        rcstring_release(replaceValue.v.stringValue);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n"
  },
  {
    "path": "core/interpreter/cmd_strings.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_strings_h\n#define cmd_strings_h\n\n#include <stdio.h>\n#include \"value.h\"\n\nstruct Core;\n\nstruct TypedValue fnc_ASC(struct Core *core);\nstruct TypedValue fnc_BIN_HEX(struct Core *core);\nstruct TypedValue fnc_CHR(struct Core *core);\nstruct TypedValue fnc_INKEY(struct Core *core);\nstruct TypedValue fnc_INSTR(struct Core *core);\nstruct TypedValue fnc_LEFTStr_RIGHTStr(struct Core *core);\nstruct TypedValue fnc_LEN(struct Core *core);\nstruct TypedValue fnc_MID(struct Core *core);\nstruct TypedValue fnc_STR(struct Core *core);\nstruct TypedValue fnc_VAL(struct Core *core);\n\nenum ErrorCode cmd_LEFT_RIGHT(struct Core *core);\nenum ErrorCode cmd_MID(struct Core *core);\n\n#endif /* cmd_strings_h */\n"
  },
  {
    "path": "core/interpreter/cmd_subs.c",
    "content": "//\n// Copyright 2018-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_subs.h\"\n#include \"core.h\"\n\nenum ErrorCode cmd_CALL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // CALL\n    struct Token *tokenCALL = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Identifier\n    if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedSubprogramName;\n    struct Token *tokenSubIdentifier = interpreter->pc;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        struct SubItem *item = tok_getSub(&interpreter->tokenizer, tokenSubIdentifier->symbolIndex);\n        if (!item) return ErrorUndefinedSubprogram;\n        tokenCALL->jumpToken = item->token;\n    }\n    \n    // optional arguments\n    int numArguments = 0;\n    if (interpreter->pc->type == TokenBracketOpen)\n    {\n        do\n        {\n            // bracket or comma\n            ++interpreter->pc;\n            \n            // argument\n            struct Token *tokens = interpreter->pc;\n            if ((interpreter->pc->type == TokenIdentifier || interpreter->pc->type == TokenStringIdentifier)\n                && tokens[1].type == TokenBracketOpen\n                && tokens[2].type == TokenBracketClose)\n            {\n                // pass array by reference\n                if (interpreter->pass == PassRun)\n                {\n                    struct ArrayVariable *variable = var_getArrayVariable(interpreter, interpreter->pc->symbolIndex, interpreter->subLevel);\n                    if (!variable) return ErrorArrayNotDimensionized;\n                    \n                    enum ErrorCode errorCode = ErrorNone;\n                    var_createArrayVariable(interpreter, &errorCode, numArguments + 1, interpreter->subLevel + 1, variable);\n                    if (errorCode != ErrorNone) return errorCode;\n                }\n                interpreter->pc += 3;\n            }\n            else\n            {\n                // expression\n                struct TypedValue value = itp_evaluateExpression(core, TypeClassAny);\n                if (value.type == ValueTypeError) return value.v.errorCode;\n                \n                if (interpreter->pass == PassRun)\n                {\n                    enum ErrorCode errorCode = ErrorNone;\n                    if (interpreter->lastVariableValue)\n                    {\n                        // pass by reference (simple variable or array element)\n                        enum ErrorCode errorCode = ErrorNone;\n                        var_createSimpleVariable(interpreter, &errorCode, numArguments + 1, interpreter->subLevel + 1, value.type, interpreter->lastVariableValue);\n                        if (errorCode != ErrorNone) return errorCode;\n                    }\n                    else\n                    {\n                        // pass by value\n                        struct SimpleVariable *variable = var_createSimpleVariable(interpreter, &errorCode, numArguments + 1, interpreter->subLevel + 1, value.type, NULL);\n                        if (!variable) return errorCode;\n                        \n                        variable->v = value.v;\n                    }\n                }\n            }\n            ++numArguments;\n        }\n        while (interpreter->pc->type == TokenComma);\n        \n        if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n\n    if (interpreter->pass == PassRun)\n    {\n        enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeCALL, interpreter->pc);\n        if (errorCode != ErrorNone) return errorCode;\n        \n        interpreter->pc = tokenCALL->jumpToken; // after sub name\n        interpreter->subLevel++;\n        \n        // parameters\n        if (interpreter->pc->type == TokenBracketOpen)\n        {\n            int parameterIndex = 0;\n            do\n            {\n                if (parameterIndex >= numArguments) return ErrorArgumentCountMismatch;\n                \n                // bracket or comma\n                ++interpreter->pc;\n                \n                // parameter\n                struct Token *tokenIdentifier = interpreter->pc;\n                if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) return ErrorSyntax;\n                enum ValueType varType = itp_getIdentifierTokenValueType(tokenIdentifier);\n\n                struct Token *nextToken = interpreter->pc + 1;\n                if (nextToken->type == TokenBracketOpen)\n                {\n                    // array\n                    struct ArrayVariable *variable = var_getArrayVariable(interpreter, parameterIndex + 1, interpreter->subLevel);\n                    if (!variable || variable->type != varType) return ErrorTypeMismatch;\n                    \n                    variable->symbolIndex = tokenIdentifier->symbolIndex;\n                    \n                    interpreter->pc += 2;\n                    \n                    if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n                    ++interpreter->pc;\n                }\n                else\n                {\n                    // simple variable\n                    struct SimpleVariable *variable = var_getSimpleVariable(interpreter, parameterIndex + 1, interpreter->subLevel);\n                    if (!variable || variable->type != varType) return ErrorTypeMismatch;\n                    \n                    variable->symbolIndex = tokenIdentifier->symbolIndex;\n                    \n                    ++interpreter->pc;\n                }\n                \n                ++parameterIndex;\n            }\n            while (interpreter->pc->type == TokenComma);\n            \n            if (parameterIndex < numArguments) return ErrorArgumentCountMismatch;\n            \n            if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n            ++interpreter->pc;\n        }\n        else if (numArguments > 0)\n        {\n            return ErrorArgumentCountMismatch;\n        }\n\n        return ErrorNone;\n    }\n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_SUB(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // SUB\n    struct Token *tokenSUB = interpreter->pc;\n    ++interpreter->pc;\n    \n    // Identifier\n    if (interpreter->pc->type != TokenIdentifier) return ErrorExpectedSubprogramName;\n    ++interpreter->pc;\n    \n    // parameters\n    if (interpreter->pc->type == TokenBracketOpen)\n    {\n        do\n        {\n            // bracket or comma\n            ++interpreter->pc;\n            \n            // parameter\n            struct Token *tokenIdentifier = interpreter->pc;\n            if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) return ErrorSyntax;\n            ++interpreter->pc;\n            \n            if (interpreter->pc->type == TokenBracketOpen)\n            {\n                ++interpreter->pc;\n                if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n                ++interpreter->pc;\n            }\n        }\n        while (interpreter->pc->type == TokenComma);\n        \n        if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n        \n    if (interpreter->pass == PassPrepare)\n    {\n        if (interpreter->numLabelStackItems > 0)\n        {\n            return ErrorSubCannotBeNested;\n        }\n        enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeSUB, tokenSUB);\n        if (errorCode != ErrorNone) return errorCode;\n        \n        interpreter->subLevel++;\n        \n        // Eol\n        if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        interpreter->pc = tokenSUB->jumpToken; // after END SUB\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_END_SUB(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // END SUB\n    ++interpreter->pc;\n    ++interpreter->pc;\n        \n    if (interpreter->pass == PassPrepare)\n    {\n        struct LabelStackItem *item = lab_popLabelStackItem(interpreter);\n        if (!item)\n        {\n            return ErrorEndSubWithoutSub;\n        }\n        else if (item->type == LabelTypeSUB)\n        {\n            item->token->jumpToken = interpreter->pc;\n        }\n        else\n        {\n            enum ErrorCode errorCode = itp_labelStackError(item);\n            return errorCode != ErrorNone ? errorCode : ErrorEndSubWithoutSub;\n        }\n        \n        // Eol\n        if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        struct LabelStackItem *itemCALL = lab_popLabelStackItem(interpreter);\n        if (!itemCALL) return ErrorEndSubWithoutSub;\n        \n        // clean local variables\n        var_freeSimpleVariables(interpreter, interpreter->subLevel);\n        var_freeArrayVariables(interpreter, interpreter->subLevel);\n        \n        if (itemCALL->type == LabelTypeONCALL)\n        {\n            // exit from interrupt\n            interpreter->exitEvaluation = true;\n        }\n        else if (itemCALL->type == LabelTypeCALL)\n        {\n            // jump back\n            interpreter->pc = itemCALL->token; // after CALL\n        }\n        else\n        {\n            return ErrorEndSubWithoutSub;\n        }\n    }\n    interpreter->subLevel--;\n    \n    return ErrorNone;\n}\n\n/*\nenum ErrorCode cmd_SHARED(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassPrepare && interpreter->subLevel == 0) return ErrorSharedOutsideOfASubprogram;\n    \n    do\n    {\n        // SHARED or comma\n        ++interpreter->pc;\n        \n        // identifier\n        struct Token *tokenIdentifier = interpreter->pc;\n        if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) return ErrorExpectedVariableIdentifier;\n        ++interpreter->pc;\n        \n        enum ValueType varType = itp_getIdentifierTokenValueType(tokenIdentifier);\n        int symbolIndex = tokenIdentifier->symbolIndex;\n        \n        if (interpreter->pc->type == TokenBracketOpen)\n        {\n            // array\n            ++interpreter->pc;\n            \n            if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n            ++interpreter->pc;\n            \n            if (interpreter->pass == PassRun)\n            {\n                struct ArrayVariable *globalVariable = var_getArrayVariable(interpreter, symbolIndex, 0);\n                if (!globalVariable) return ErrorArrayNotDimensionized;\n                \n                enum ErrorCode errorCode = ErrorNone;\n                var_createArrayVariable(interpreter, &errorCode, symbolIndex, interpreter->subLevel, globalVariable);\n                if (errorCode != ErrorNone) return errorCode;\n            }\n        }\n        else\n        {\n            // simple variable\n            if (interpreter->pass == PassRun)\n            {\n                struct SimpleVariable *globalVariable = var_getSimpleVariable(interpreter, symbolIndex, 0);\n                if (!globalVariable) return ErrorVariableNotInitialized;\n                \n                enum ErrorCode errorCode = ErrorNone;\n                var_createSimpleVariable(interpreter, &errorCode, symbolIndex, interpreter->subLevel, varType, &globalVariable->v);\n                if (errorCode != ErrorNone) return errorCode;\n            }\n        }\n    }\n    while (interpreter->pc->type == TokenComma);\n    \n    return itp_endOfCommand(interpreter);\n}\n*/\n\nenum ErrorCode cmd_GLOBAL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassPrepare && interpreter->subLevel > 0) return ErrorGlobalInsideOfASubprogram;\n    \n    do\n    {\n        // GLOBAL or comma\n        ++interpreter->pc;\n        \n        // identifier\n        struct Token *tokenIdentifier = interpreter->pc;\n        if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier) return ErrorSyntax;\n        ++interpreter->pc;\n        \n        int symbolIndex = tokenIdentifier->symbolIndex;\n        \n        if (interpreter->pass == PassRun)\n        {\n            struct SimpleVariable *variable = var_getSimpleVariable(interpreter, symbolIndex, 0);\n            if (variable)\n            {\n                variable->subLevel = SUB_LEVEL_GLOBAL;\n            }\n            else\n            {\n                enum ValueType varType = itp_getIdentifierTokenValueType(tokenIdentifier);\n                enum ErrorCode errorCode = ErrorNone;\n                variable = var_createSimpleVariable(interpreter, &errorCode, symbolIndex, SUB_LEVEL_GLOBAL, varType, NULL);\n                if (!variable) return errorCode;\n            }\n        }\n    }\n    while (interpreter->pc->type == TokenComma);\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_EXIT_SUB(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // EXIT\n    ++interpreter->pc;\n    \n    // SUB\n    if (interpreter->pc->type != TokenSUB) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassPrepare)\n    {\n        if (interpreter->subLevel == 0) return ErrorExitSubOutsideOfASubprogram;\n        return itp_endOfCommand(interpreter);\n    }\n    else if (interpreter->pass == PassRun)\n    {\n        struct LabelStackItem *itemCALL = lab_popLabelStackItem(interpreter);\n        if (!itemCALL) return ErrorExitSubOutsideOfASubprogram;\n        \n        // clean local variables\n        var_freeSimpleVariables(interpreter, interpreter->subLevel);\n        var_freeArrayVariables(interpreter, interpreter->subLevel);\n        \n        if (itemCALL->type == LabelTypeONCALL)\n        {\n            // exit from interrupt\n            interpreter->exitEvaluation = true;\n        }\n        else if (itemCALL->type == LabelTypeCALL)\n        {\n            // jump back\n            interpreter->pc = itemCALL->token; // after CALL\n        }\n        else\n        {\n            return ErrorExitSubOutsideOfASubprogram;\n        }\n        interpreter->subLevel--;\n    }\n    return ErrorNone;\n}\n"
  },
  {
    "path": "core/interpreter/cmd_subs.h",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_subs_h\n#define cmd_subs_h\n\n#include <stdio.h>\n#include \"error.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_CALL(struct Core *core);\nenum ErrorCode cmd_SUB(struct Core *core);\nenum ErrorCode cmd_END_SUB(struct Core *core);\n//enum ErrorCode cmd_SHARED(struct Core *core);\nenum ErrorCode cmd_GLOBAL(struct Core *core);\nenum ErrorCode cmd_EXIT_SUB(struct Core *core);\n\n#endif /* cmd_subs_h */\n"
  },
  {
    "path": "core/interpreter/cmd_text.c",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_text.h\"\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n#include <assert.h>\n#include \"core.h\"\n#include \"text_lib.h\"\n\nenum ErrorCode cmd_PRINT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    struct TextLib *lib = &interpreter->textLib;\n    \n    bool newLine = true;\n    \n    // PRINT\n    ++interpreter->pc;\n    \n    while (!itp_isEndOfCommand(interpreter))\n    {\n        struct TypedValue value = itp_evaluateExpression(core, TypeClassAny);\n        if (value.type == ValueTypeError) return value.v.errorCode;\n        \n        if (interpreter->pass == PassRun)\n        {\n            if (value.type == ValueTypeString)\n            {\n                txtlib_printText(lib, value.v.stringValue->chars);\n            }\n            else if (value.type == ValueTypeFloat)\n            {\n                char buffer[20];\n                snprintf(buffer, 20, \"%0.7g\", value.v.floatValue);\n                txtlib_printText(lib, buffer);\n            }\n        }\n        \n        if (interpreter->pc->type == TokenComma)\n        {\n            if (interpreter->pass == PassRun)\n            {\n                txtlib_printText(lib, \" \");\n            }\n            ++interpreter->pc;\n            newLine = false;\n        }\n        else if (interpreter->pc->type == TokenSemicolon)\n        {\n            ++interpreter->pc;\n            newLine = false;\n        }\n        else if (itp_isEndOfCommand(interpreter))\n        {\n            newLine = true;\n        }\n        else\n        {\n            return ErrorSyntax;\n        }\n    }\n    \n    if (interpreter->pass == PassRun && newLine)\n    {\n        txtlib_printText(lib, \"\\n\");\n    }\n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_INPUT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    struct TextLib *lib = &interpreter->textLib;\n    \n    // INPUT\n    ++interpreter->pc;\n    \n    if (interpreter->pc->type == TokenString)\n    {\n        // prompt\n        if (interpreter->pass == PassRun)\n        {\n            txtlib_printText(lib, interpreter->pc->stringValue->chars);\n        }\n        ++interpreter->pc;\n        \n        // semicolon\n        if (interpreter->pc->type != TokenSemicolon) return ErrorSyntax;\n        ++interpreter->pc;\n    }\n    \n    if (interpreter->pass == PassRun)\n    {\n        txtlib_inputBegin(lib);\n        interpreter->state = StateInput;\n    }\n    else\n    {\n        return cmd_endINPUT(core);\n    }\n    \n    return ErrorNone;\n}\n\nenum ErrorCode cmd_endINPUT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // identifier\n    enum ErrorCode errorCode = ErrorNone;\n    enum ValueType valueType = ValueTypeNull;\n    union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true);\n    if (!varValue) return errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (valueType == ValueTypeString)\n        {\n            struct RCString *rcstring = rcstring_new(interpreter->textLib.inputBuffer, interpreter->textLib.inputLength);\n            if (!rcstring) return ErrorOutOfMemory;\n            \n            if (varValue->stringValue)\n            {\n                rcstring_release(varValue->stringValue);\n            }\n            varValue->stringValue = rcstring;\n        }\n        else if (valueType == ValueTypeFloat)\n        {\n            varValue->floatValue = atof(interpreter->textLib.inputBuffer);\n        }\n    }\n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_TEXT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // TEXT\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n\n    // y value\n    struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n\n    // string value\n    struct TypedValue stringValue = itp_evaluateExpression(core, TypeClassString);\n    if (stringValue.type == ValueTypeError) return stringValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        struct TextLib *lib = &interpreter->textLib;\n        txtlib_writeText(lib, stringValue.v.stringValue->chars, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue));\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_NUMBER(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // NUMBER\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // number value\n    struct TypedValue numberValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (numberValue.type == ValueTypeError) return numberValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // digits value\n    struct TypedValue digitsValue = itp_evaluateExpression(core, TypeClassNumeric);\n    if (digitsValue.type == ValueTypeError) return digitsValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        int digits = digitsValue.v.floatValue;\n        struct TextLib *lib = &interpreter->textLib;\n        txtlib_writeNumber(lib, numberValue.v.floatValue, digits, floorf(xValue.v.floatValue), floorf(yValue.v.floatValue));\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_CLS(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    struct TextLib *lib = &interpreter->textLib;\n    \n    // CLS\n    ++interpreter->pc;\n    \n    if (itp_isEndOfCommand(interpreter))\n    {\n        if (interpreter->pass == PassRun)\n        {\n            // clear all\n            txtlib_clearScreen(lib);\n        }\n    }\n    else\n    {\n        // bg value\n        struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1);\n        if (bgValue.type == ValueTypeError) return bgValue.v.errorCode;\n        \n        if (interpreter->pass == PassRun)\n        {\n            // clear bg\n            txtlib_clearBackground(lib, bgValue.v.floatValue);\n        }\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_WINDOW(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    // WINDOW\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateNumericExpression(core, 0, PLANE_COLUMNS - 1);\n    if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateNumericExpression(core, 0, PLANE_ROWS - 1);\n    if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // w value\n    struct TypedValue wValue = itp_evaluateNumericExpression(core, 1, PLANE_COLUMNS);\n    if (wValue.type == ValueTypeError) return wValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // h value\n    struct TypedValue hValue = itp_evaluateNumericExpression(core, 1, PLANE_ROWS);\n    if (hValue.type == ValueTypeError) return hValue.v.errorCode;\n\n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // bg value\n    struct TypedValue bgValue = itp_evaluateNumericExpression(core, 0, 1);\n    if (bgValue.type == ValueTypeError) return bgValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        core->interpreter->textLib.windowX = xValue.v.floatValue;\n        core->interpreter->textLib.windowY = yValue.v.floatValue;\n        core->interpreter->textLib.windowWidth = wValue.v.floatValue;\n        core->interpreter->textLib.windowHeight = hValue.v.floatValue;\n        core->interpreter->textLib.bg = bgValue.v.floatValue;\n        core->interpreter->textLib.cursorX = 0;\n        core->interpreter->textLib.cursorY = 0;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_FONT(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // FONT\n    ++interpreter->pc;\n    \n    // char value\n    struct TypedValue cValue = itp_evaluateNumericExpression(core, 0, NUM_CHARACTERS - 1);\n    if (cValue.type == ValueTypeError) return cValue.v.errorCode;\n    \n    if (interpreter->pass == PassRun)\n    {\n        interpreter->textLib.fontCharOffset = cValue.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_LOCATE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    // LOCATE\n    ++interpreter->pc;\n    \n    // x value\n    struct TypedValue xValue = itp_evaluateNumericExpression(core, 0, core->interpreter->textLib.windowWidth - 1);\n    if (xValue.type == ValueTypeError) return xValue.v.errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y value\n    struct TypedValue yValue = itp_evaluateNumericExpression(core, 0, core->interpreter->textLib.windowHeight - 1);\n    if (yValue.type == ValueTypeError) return yValue.v.errorCode;\n\n    if (interpreter->pass == PassRun)\n    {\n        core->interpreter->textLib.cursorX = xValue.v.floatValue;\n        core->interpreter->textLib.cursorY = yValue.v.floatValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_CURSOR(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // CURSOR.?\n    enum TokenType type = interpreter->pc->type;\n    ++interpreter->pc;\n        \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        switch (type)\n        {\n            case TokenCURSORX:\n                value.v.floatValue = interpreter->textLib.cursorX;\n                break;\n                \n            case TokenCURSORY:\n                value.v.floatValue = interpreter->textLib.cursorY;\n                break;\n                \n            default:\n                assert(0);\n                break;\n        }\n    }\n    return value;\n}\n\nenum ErrorCode cmd_CLW(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    // CLW\n    ++interpreter->pc;\n    \n    if (interpreter->pass == PassRun)\n    {\n        txtlib_clearWindow(&interpreter->textLib);\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_TRACE(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    struct TextLib *lib = &core->overlay->textLib;\n    bool debug = interpreter->debug;\n    \n    do\n    {\n        // TRACE or comma\n        bool separate = (interpreter->pc->type == TokenComma);\n        ++interpreter->pc;\n        \n        struct TypedValue value = itp_evaluateExpression(core, TypeClassAny);\n        if (value.type == ValueTypeError) return value.v.errorCode;\n        \n        if (interpreter->pass == PassRun)\n        {\n            if (separate && debug)\n            {\n                txtlib_printText(lib, \" \");\n            }\n            if (value.type == ValueTypeString)\n            {\n                if (debug)\n                {\n                    txtlib_printText(lib, value.v.stringValue->chars);\n                }\n                rcstring_release(value.v.stringValue);\n            }\n            else if (value.type == ValueTypeFloat)\n            {\n                if (debug)\n                {\n                    char buffer[20];\n                    snprintf(buffer, 20, \"%0.7g\", value.v.floatValue);\n                    txtlib_printText(lib, buffer);\n                }\n            }\n        }\n    }\n    while (interpreter->pc->type == TokenComma);\n    \n    if (interpreter->pass == PassRun && debug)\n    {\n        txtlib_printText(lib, \"\\n\");\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\n"
  },
  {
    "path": "core/interpreter/cmd_text.h",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_text_h\n#define cmd_text_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"error.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_PRINT(struct Core *core);\nenum ErrorCode cmd_INPUT(struct Core *core);\nenum ErrorCode cmd_endINPUT(struct Core *core);\nenum ErrorCode cmd_TEXT(struct Core *core);\nenum ErrorCode cmd_NUMBER(struct Core *core);\nenum ErrorCode cmd_CLS(struct Core *core);\nenum ErrorCode cmd_WINDOW(struct Core *core);\nenum ErrorCode cmd_FONT(struct Core *core);\nenum ErrorCode cmd_LOCATE(struct Core *core);\nstruct TypedValue fnc_CURSOR(struct Core *core);\nenum ErrorCode cmd_CLW(struct Core *core);\nenum ErrorCode cmd_TRACE(struct Core *core);\n\n#endif /* cmd_text_h */\n"
  },
  {
    "path": "core/interpreter/cmd_variables.c",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"cmd_variables.h\"\n#include \"core.h\"\n\nenum ErrorCode cmd_LET(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // LET keyword is optional\n    if (interpreter->pc->type == TokenLET)\n    {\n        ++interpreter->pc;\n        if (interpreter->pc->type != TokenIdentifier && interpreter->pc->type != TokenStringIdentifier) return ErrorSyntax;\n    }\n    \n    // identifier\n    enum ErrorCode errorCode = ErrorNone;\n    enum ValueType valueType = ValueTypeNull;\n    union Value *varValue = itp_readVariable(core, &valueType, &errorCode, true);\n    if (!varValue) return errorCode;\n    if (interpreter->pc->type != TokenEq) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // value\n    struct TypedValue value = itp_evaluateExpression(core, TypeClassAny);\n    if (value.type == ValueTypeError) return value.v.errorCode;\n    if (value.type != valueType) return ErrorTypeMismatch;\n    \n    if (interpreter->pass == PassRun)\n    {\n        if (valueType == ValueTypeString && varValue->stringValue)\n        {\n            rcstring_release(varValue->stringValue);\n        }\n        *varValue = value.v;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n\nenum ErrorCode cmd_DIM(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pass == PassRun && interpreter->mode == ModeInterrupt) return ErrorNotAllowedInInterrupt;\n    \n    bool isGlobal = false;\n    struct Token *nextToken = interpreter->pc + 1;\n    if (nextToken->type == TokenGLOBAL)\n    {\n        ++interpreter->pc;\n        if (interpreter->pass == PassPrepare && interpreter->subLevel > 0) return ErrorGlobalInsideOfASubprogram;\n        isGlobal = true;\n    }\n    \n    do\n    {\n        // DIM, GLOBAL or comma\n        ++interpreter->pc;\n        \n        // identifier\n        struct Token *tokenIdentifier = interpreter->pc;\n        ++interpreter->pc;\n        if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier)\n        {\n            return ErrorSyntax;\n        }\n        \n        int numDimensions = 0;\n        int dimensionSizes[MAX_ARRAY_DIMENSIONS];\n        \n        if (interpreter->pc->type != TokenBracketOpen) return ErrorSyntax;\n        ++interpreter->pc;\n        \n        for (int i = 0; i < MAX_ARRAY_DIMENSIONS; i++)\n        {\n            struct TypedValue value = itp_evaluateExpression(core, TypeClassNumeric);\n            if (value.type == ValueTypeError) return value.v.errorCode;\n            \n            dimensionSizes[i] = value.v.floatValue + 1; // value is max index, so size is +1\n            numDimensions++;\n            \n            if (interpreter->pc->type == TokenComma)\n            {\n                ++interpreter->pc;\n            }\n            else\n            {\n                break;\n            }\n        }\n        \n        if (interpreter->pc->type != TokenBracketClose) return ErrorSyntax;\n        ++interpreter->pc;\n        \n        if (interpreter->pass == PassRun)\n        {\n            enum ErrorCode errorCode = ErrorNone;\n            struct ArrayVariable *variable = var_dimVariable(interpreter, &errorCode, tokenIdentifier->symbolIndex, numDimensions, dimensionSizes);\n            if (!variable) return errorCode;\n            variable->type = (tokenIdentifier->type == TokenStringIdentifier) ? ValueTypeString : ValueTypeFloat;\n            if (isGlobal)\n            {\n                variable->subLevel = SUB_LEVEL_GLOBAL;\n            }\n            interpreter->cycles += variable->numValues;\n        }\n    }\n    while (interpreter->pc->type == TokenComma);\n    \n    return itp_endOfCommand(interpreter);\n}\n\nstruct TypedValue fnc_UBOUND(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // UBOUND\n    ++interpreter->pc;\n    \n    // bracket open\n    if (interpreter->pc->type != TokenBracketOpen) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    // array\n    if (interpreter->pc->type != TokenIdentifier && interpreter->pc->type != TokenStringIdentifier) return val_makeError(ErrorSyntax);\n    int symbolIndex = interpreter->pc->symbolIndex;\n    ++interpreter->pc;\n    \n    int d = 0;\n    if (interpreter->pc->type == TokenComma)\n    {\n        // comma\n        ++interpreter->pc;\n        \n        // dimension value\n        struct TypedValue dValue = itp_evaluateNumericExpression(core, 1, MAX_ARRAY_DIMENSIONS);\n        if (dValue.type == ValueTypeError) return val_makeError(dValue.v.errorCode);\n        \n        d = dValue.v.floatValue - 1;\n    }\n    \n    // bracket close\n    if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n    ++interpreter->pc;\n    \n    struct TypedValue value;\n    value.type = ValueTypeFloat;\n    \n    if (interpreter->pass == PassRun)\n    {\n        struct ArrayVariable *variable = var_getArrayVariable(interpreter, symbolIndex, interpreter->subLevel);\n        if (!variable) return val_makeError(ErrorArrayNotDimensionized);\n        \n        value.v.floatValue = variable->dimensionSizes[d] - 1;\n    }\n    return value;\n}\n\nenum ErrorCode cmd_SWAP(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n\n    // SWAP\n    ++interpreter->pc;\n    \n    enum ErrorCode errorCode = ErrorNone;\n    \n    // x identifier\n    enum ValueType xValueType = ValueTypeNull;\n    union Value *xVarValue = itp_readVariable(core, &xValueType, &errorCode, false);\n    if (!xVarValue) return errorCode;\n    \n    // comma\n    if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n    ++interpreter->pc;\n    \n    // y identifier\n    enum ValueType yValueType = ValueTypeNull;\n    union Value *yVarValue = itp_readVariable(core, &yValueType, &errorCode, false);\n    if (!yVarValue) return errorCode;\n    \n    if (xValueType != yValueType) return ErrorTypeMismatch;\n    \n    if (interpreter->pass == PassRun)\n    {\n        union Value spareValue = *xVarValue;\n        *xVarValue = *yVarValue;\n        *yVarValue = spareValue;\n    }\n    \n    return itp_endOfCommand(interpreter);\n}\n"
  },
  {
    "path": "core/interpreter/cmd_variables.h",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef cmd_variables_h\n#define cmd_variables_h\n\n#include <stdio.h>\n#include \"error.h\"\n\nstruct Core;\n\nenum ErrorCode cmd_LET(struct Core *core);\nenum ErrorCode cmd_DIM(struct Core *core);\nstruct TypedValue fnc_UBOUND(struct Core *core);\nenum ErrorCode cmd_SWAP(struct Core *core);\n\n#endif /* cmd_variables_h */\n"
  },
  {
    "path": "core/interpreter/data.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"data.h\"\n#include \"interpreter.h\"\n\nvoid dat_nextData(struct Interpreter *interpreter)\n{\n    interpreter->currentDataValueToken++;\n    if (interpreter->currentDataValueToken->type == TokenComma)\n    {\n        // value follows\n        interpreter->currentDataValueToken++;\n    }\n    else\n    {\n        // next DATA line\n        interpreter->currentDataToken = interpreter->currentDataToken->jumpToken;\n        if (interpreter->currentDataToken)\n        {\n            interpreter->currentDataValueToken = interpreter->currentDataToken + 1; // after DATA\n        }\n        else\n        {\n            interpreter->currentDataValueToken = NULL;\n        }\n    }\n}\n\nvoid dat_restoreData(struct Interpreter *interpreter, struct Token *jumpToken)\n{\n    if (jumpToken)\n    {\n        struct Token *dataToken = interpreter->firstData;\n        while (dataToken && dataToken < jumpToken)\n        {\n            dataToken = dataToken->jumpToken;\n        }\n        interpreter->currentDataToken = dataToken;\n    }\n    else\n    {\n        interpreter->currentDataToken = interpreter->firstData;\n    }\n    \n    if (interpreter->currentDataToken)\n    {\n        interpreter->currentDataValueToken = interpreter->currentDataToken + 1; // after DATA\n    }\n    else\n    {\n        interpreter->currentDataValueToken = NULL;\n    }\n}\n"
  },
  {
    "path": "core/interpreter/data.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef data_h\n#define data_h\n\n#include <stdio.h>\n\nstruct Interpreter;\nstruct Token;\n\nvoid dat_nextData(struct Interpreter *interpreter);\nvoid dat_restoreData(struct Interpreter *interpreter, struct Token *jumpToken);\n\n#endif /* data_h */\n"
  },
  {
    "path": "core/interpreter/error.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"error.h\"\n\nconst char *ErrorStrings[] = {\n    \"OK\",\n    \n    \"Could Not Open Program\",\n    \"Too Many Tokens\",\n    \"ROM Is Full\",\n    \"Index Already Defined\",\n    \"Unterminated String\",\n    \"Unexpected Character\",\n    \"Reserved Keyword\",\n    \"Syntax Error\",\n    \"Symbol Name Too Long\",\n    \"Too Many Symbols\",\n    \"Type Mismatch\",\n    \"Out Of Memory\",\n    \"ELSE Without IF\",\n    \"END IF Without IF\",\n    \"Expected Command\",\n    \"NEXT Without FOR\",\n    \"LOOP Without DO\",\n    \"UNTIL Without REPEAT\",\n    \"WEND Without WHILE\",\n    \"Label Already Defined\",\n    \"Too Many Labels\",\n    \"ErrorExpectedLabel\",\n    \"Undefined Label\",\n    \"Array Not Dimensionized\",\n    \"Array Already Dimensionized\",\n    \"Variable Already Used\",\n    \"Index Out Of Bounds\",\n    \"Wrong Number Of Dimensions\",\n    \"Invalid Parameter\",\n    \"RETURN Without GOSUB\",\n    \"Stack Overflow\",\n    \"Out Of Data\",\n    \"Illegal Memory Access\",\n    \"Too Many CPU Cycles In Interrupt\",\n    \"Not Allowed In Interrupt\",\n    \"IF Without END IF\",\n    \"FOR Without NEXT\",\n    \"DO Without LOOP\",\n    \"REPEAT Without UNTIL\",\n    \"WHILE Without WEND\",\n    \"EXIT Not Inside Loop\",\n    \"Directory Not Loaded\",\n    \"Division By Zero\",\n    \"Variable Not Initialized\",\n    \"Array Variable Without Index\",\n    \"END SUB Without SUB\",\n    \"SUB Without END SUB\",\n    \"SUB Cannot Be Nested\",\n    \"Undefined Subprogram\",\n    \"Expected Subprogram Name\",\n    \"Argument Count Mismatch\",\n    \"SUB Already Defined\",\n    \"Too Many Subprograms\",\n    \"SHARED Outside Of A Subprogram\",\n    \"GLOBAL Inside Of A Subprogram\",\n    \"EXIT SUB Outside Of A Subprogram\",\n    \"Keyboard Not Enabled\",\n    \"Automatic Pause Not Disabled\",\n    \"Gamepad Not Enabled\",\n    \"Touch Not Enabled\",\n    \"Input Change Not Allowed\",\n};\n\nconst char *err_getString(enum ErrorCode errorCode)\n{\n    return ErrorStrings[errorCode];\n}\n\nstruct CoreError err_makeCoreError(enum ErrorCode code, int sourcePosition)\n{\n    struct CoreError error = {code, sourcePosition};\n    return error;\n}\n\nstruct CoreError err_noCoreError(void)\n{\n    struct CoreError error = {ErrorNone, 0};\n    return error;\n}\n"
  },
  {
    "path": "core/interpreter/error.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef error_h\n#define error_h\n\n#include <stdio.h>\n\nenum ErrorCode {\n    ErrorNone,\n    \n    ErrorCouldNotOpenProgram,\n    ErrorTooManyTokens,\n    ErrorRomIsFull,\n    ErrorIndexAlreadyDefined,\n    ErrorUnterminatedString,\n    ErrorUnexpectedCharacter,\n    ErrorReservedKeyword,\n    ErrorSyntax,\n    ErrorSymbolNameTooLong,\n    ErrorTooManySymbols,\n    ErrorTypeMismatch,\n    ErrorOutOfMemory,\n    ErrorElseWithoutIf,\n    ErrorEndIfWithoutIf,\n    ErrorExpectedCommand,\n    ErrorNextWithoutFor,\n    ErrorLoopWithoutDo,\n    ErrorUntilWithoutRepeat,\n    ErrorWendWithoutWhile,\n    ErrorLabelAlreadyDefined,\n    ErrorTooManyLabels,\n    ErrorExpectedLabel,\n    ErrorUndefinedLabel,\n    ErrorArrayNotDimensionized,\n    ErrorArrayAlreadyDimensionized,\n    ErrorVariableAlreadyUsed,\n    ErrorIndexOutOfBounds,\n    ErrorWrongNumberOfDimensions,\n    ErrorInvalidParameter,\n    ErrorReturnWithoutGosub,\n    ErrorStackOverflow,\n    ErrorOutOfData,\n    ErrorIllegalMemoryAccess,\n    ErrorTooManyCPUCyclesInInterrupt,\n    ErrorNotAllowedInInterrupt,\n    ErrorIfWithoutEndIf,\n    ErrorForWithoutNext,\n    ErrorDoWithoutLoop,\n    ErrorRepeatWithoutUntil,\n    ErrorWhileWithoutWend,\n    ErrorExitNotInsideLoop,\n    ErrorDirectoryNotLoaded,\n    ErrorDivisionByZero,\n    ErrorVariableNotInitialized,\n    ErrorArrayVariableWithoutIndex,\n    ErrorEndSubWithoutSub,\n    ErrorSubWithoutEndSub,\n    ErrorSubCannotBeNested,\n    ErrorUndefinedSubprogram,\n    ErrorExpectedSubprogramName,\n    ErrorArgumentCountMismatch,\n    ErrorSubAlreadyDefined,\n    ErrorTooManySubprograms,\n    ErrorSharedOutsideOfASubprogram,\n    ErrorGlobalInsideOfASubprogram,\n    ErrorExitSubOutsideOfASubprogram,\n    ErrorKeyboardNotEnabled,\n    ErrorAutomaticPauseNotDisabled,\n    ErrorGamepadNotEnabled,\n    ErrorTouchNotEnabled,\n    ErrorInputChangeNotAllowed,\n};\n\nstruct CoreError {\n    enum ErrorCode code;\n    int sourcePosition;\n};\n\nconst char *err_getString(enum ErrorCode errorCode);\nstruct CoreError err_makeCoreError(enum ErrorCode code, int sourcePosition);\nstruct CoreError err_noCoreError(void);\n\n#endif /* error_h */\n"
  },
  {
    "path": "core/interpreter/interpreter.c",
    "content": "//\n// Copyright 2016-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"interpreter.h\"\n#include <stdlib.h>\n#include <assert.h>\n#include <string.h>\n#include <math.h>\n#include <stdint.h>\n#include \"core.h\"\n#include \"default_characters.h\"\n#include \"cmd_audio.h\"\n#include \"cmd_control.h\"\n#include \"cmd_variables.h\"\n#include \"cmd_data.h\"\n#include \"cmd_strings.h\"\n#include \"cmd_memory.h\"\n#include \"cmd_text.h\"\n#include \"cmd_maths.h\"\n#include \"cmd_background.h\"\n#include \"cmd_screen.h\"\n#include \"cmd_sprites.h\"\n#include \"cmd_io.h\"\n#include \"cmd_files.h\"\n#include \"cmd_subs.h\"\n#include \"string_utils.h\"\n\nstruct TypedValue itp_evaluateExpressionLevel(struct Core *core, int level);\nstruct TypedValue itp_evaluatePrimaryExpression(struct Core *core);\nstruct TypedValue itp_evaluateFunction(struct Core *core);\nenum ErrorCode itp_evaluateCommand(struct Core *core);\n\nvoid itp_init(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    interpreter->romDataManager.data = core->machine->cartridgeRom;\n    \n    // global null string\n    interpreter->nullString = rcstring_new(NULL, 0);\n    if (!interpreter->nullString) exit(EXIT_FAILURE);\n}\n\nvoid itp_deinit(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    itp_freeProgram(core);\n    \n    // Free null string\n    if (interpreter->nullString)\n    {\n        rcstring_release(interpreter->nullString);\n        interpreter->nullString = NULL;\n    }\n}\n\nstruct CoreError itp_compileProgram(struct Core *core, const char *sourceCode)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    itp_freeProgram(core);\n    \n    // Parse source code\n    \n    interpreter->sourceCode = uppercaseString(sourceCode);\n    if (!interpreter->sourceCode) return err_makeCoreError(ErrorOutOfMemory, -1);\n    \n    struct CoreError error = tok_tokenizeUppercaseProgram(&interpreter->tokenizer, interpreter->sourceCode);\n    if (error.code != ErrorNone)\n    {\n        return error;\n    }\n    \n    struct DataManager *romDataManager = &interpreter->romDataManager;\n    error = data_uppercaseImport(romDataManager, interpreter->sourceCode, false);\n    if (error.code != ErrorNone) return error;\n\n    // add default characters if ROM entry 0 is unused\n    struct DataEntry *entry0 = &romDataManager->entries[0];\n    if (entry0->length == 0 && (DATA_SIZE - data_currentSize(romDataManager)) >= 1024)\n    {\n        data_setEntry(romDataManager, 0, \"FONT\", (uint8_t *)DefaultCharacters, 1024);\n    }\n    \n    // Prepare commands\n    \n    interpreter->pc = interpreter->tokenizer.tokens;\n    interpreter->pass = PassPrepare;\n    interpreter->exitEvaluation = false;\n    interpreter->subLevel = 0;\n    interpreter->numLabelStackItems = 0;\n    interpreter->isSingleLineIf = false;\n    \n    enum ErrorCode errorCode;\n    do\n    {\n        errorCode = itp_evaluateCommand(core);\n    }\n    while (errorCode == ErrorNone && interpreter->pc->type != TokenUndefined);\n    \n    if (errorCode != ErrorNone) return err_makeCoreError(errorCode, interpreter->pc->sourcePosition);\n    \n    if (interpreter->numLabelStackItems > 0)\n    {\n        struct LabelStackItem *item = &interpreter->labelStackItems[interpreter->numLabelStackItems - 1];\n        errorCode = itp_labelStackError(item);\n        if (errorCode != ErrorNone)\n        {\n            return err_makeCoreError(errorCode, item->token->sourcePosition);\n        }\n    }\n    \n    // prepare for run\n    \n    interpreter->pc = interpreter->tokenizer.tokens;\n    interpreter->cycles = 0;\n    interpreter->interruptOverCycles = 0;\n    interpreter->pass = PassRun;\n    interpreter->state = StateEvaluate;\n    interpreter->mode = ModeNone;\n    interpreter->handlesPause = true;\n    interpreter->currentDataToken = interpreter->firstData;\n    interpreter->currentDataValueToken = interpreter->firstData ? interpreter->firstData + 1 : NULL;\n    interpreter->isSingleLineIf = false;\n    interpreter->lastFrameIOStatus.value = 0;\n    interpreter->seed = 0;\n    interpreter->isKeyboardOptional = false;\n    \n    memset(&interpreter->textLib, 0, sizeof(struct TextLib));\n    memset(&interpreter->spritesLib, 0, sizeof(struct SpritesLib));\n    memset(&interpreter->audioLib, 0, sizeof(struct AudioLib));\n    interpreter->textLib.core = core;\n    interpreter->spritesLib.core = core;\n    interpreter->audioLib.core = core;\n\n    return err_noCoreError();\n}\n\nvoid itp_runProgram(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    switch (interpreter->state)\n    {\n        case StateEvaluate:\n        {\n            if (interpreter->waitCount > 0)\n            {\n                --interpreter->waitCount;\n                break;\n            }\n            \n            interpreter->mode = ModeMain;\n            interpreter->exitEvaluation = false;\n            enum ErrorCode errorCode = ErrorNone;\n            \n            while (   errorCode == ErrorNone\n                   && interpreter->cycles < MAX_CYCLES_TOTAL_PER_FRAME\n                   && interpreter->state == StateEvaluate\n                   && !interpreter->exitEvaluation)\n            {\n                errorCode = itp_evaluateCommand(core);\n            }\n            \n            if (interpreter->cycles >= MAX_CYCLES_TOTAL_PER_FRAME)\n            {\n                machine_suspendEnergySaving(core, 2);\n            }\n            \n            interpreter->mode = ModeNone;\n            if (errorCode != ErrorNone)\n            {\n                itp_endProgram(core);\n                delegate_interpreterDidFail(core, err_makeCoreError(errorCode, interpreter->pc->sourcePosition));\n            }\n            break;\n        }\n            \n        case StateInput:\n        {\n            if (txtlib_inputUpdate(&interpreter->textLib))\n            {\n                interpreter->state = StateEvaluate;\n                cmd_endINPUT(core);\n            }\n            break;\n        }\n            \n        case StateNoProgram:\n        case StatePaused:\n        case StateEnd:\n        case StateWaitForDisk:\n            break;\n    }\n}\n\nvoid itp_runInterrupt(struct Core *core, enum InterruptType type)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    switch (interpreter->state)\n    {\n        case StateEvaluate:\n        case StateInput:\n        case StatePaused:\n        case StateWaitForDisk:\n        {\n            struct Token *startToken = NULL;\n            int maxCycles;\n            \n            int mainCycles = interpreter->cycles;\n            interpreter->cycles = 0;\n            \n            switch (type)\n            {\n                case InterruptTypeRaster:\n                    startToken = interpreter->currentOnRasterToken;\n                    maxCycles = MAX_CYCLES_PER_RASTER;\n                    break;\n                    \n                case InterruptTypeVBL:\n                    startToken = interpreter->currentOnVBLToken;\n                    maxCycles = MAX_CYCLES_PER_VBL;\n                    // update audio player\n                    audlib_update(&interpreter->audioLib);\n                    break;\n            }\n            \n            if (startToken)\n            {\n                interpreter->mode = ModeInterrupt;\n                interpreter->exitEvaluation = false;\n                struct Token *pc = interpreter->pc;\n                interpreter->pc = startToken;\n                interpreter->subLevel++;\n                \n                enum ErrorCode errorCode = lab_pushLabelStackItem(interpreter, LabelTypeONCALL, NULL);\n                \n                while (   errorCode == ErrorNone\n                       // cycles can exceed interrupt limit (see interruptOverCycles), but there is still a hard limit for extreme cases\n                       && interpreter->cycles < MAX_CYCLES_TOTAL_PER_FRAME\n                       && !interpreter->exitEvaluation)\n                {\n                    errorCode = itp_evaluateCommand(core);\n                }\n                \n                interpreter->mode = ModeNone;\n                \n                if (interpreter->cycles >= MAX_CYCLES_TOTAL_PER_FRAME)\n                {\n                    itp_endProgram(core);\n                    delegate_interpreterDidFail(core, err_makeCoreError(ErrorTooManyCPUCyclesInInterrupt, interpreter->pc->sourcePosition));\n                }\n                else if (errorCode != ErrorNone)\n                {\n                    itp_endProgram(core);\n                    delegate_interpreterDidFail(core, err_makeCoreError(errorCode, interpreter->pc->sourcePosition));\n                }\n                else\n                {\n                    interpreter->pc = pc;\n                }\n            }\n            \n            // calculate cycles exceeding limit\n            interpreter->interruptOverCycles += interpreter->cycles - maxCycles;\n            if (interpreter->interruptOverCycles < 0)\n            {\n                interpreter->interruptOverCycles = 0;\n            }\n            \n            // sum of interrupt's and main cycle count\n            interpreter->cycles += mainCycles;\n            \n            break;\n        }\n            \n        case StateNoProgram:\n        case StateEnd:\n            break;\n    }\n\n}\n\nvoid itp_didFinishVBL(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // remember this frame's IO\n    for (int i = 0; i < NUM_GAMEPADS; i++)\n    {\n        interpreter->lastFrameGamepads[i] = core->machine->ioRegisters.gamepads[i];\n    }\n    interpreter->lastFrameIOStatus = core->machine->ioRegisters.status;\n    \n    // timer\n    interpreter->timer++;\n    if (interpreter->timer >= TIMER_WRAP_VALUE)\n    {\n        interpreter->timer = 0;\n    }\n    \n    // pause\n    if (core->machine->ioRegisters.status.pause)\n    {\n        if (interpreter->handlesPause && interpreter->state == StateEvaluate)\n        {\n            interpreter->state = StatePaused;\n            overlay_updateState(core);\n            core->machine->ioRegisters.status.pause = 0;\n        }\n        else if (interpreter->state == StatePaused)\n        {\n            interpreter->state = StateEvaluate;\n            overlay_updateState(core);\n            core->machine->ioRegisters.status.pause = 0;\n        }\n    }\n    \n    // CPU load (rounded up)\n    int currentCpuLoad = (interpreter->cycles * 100 + MAX_CYCLES_TOTAL_PER_FRAME - 1) / MAX_CYCLES_TOTAL_PER_FRAME;\n    if (currentCpuLoad > interpreter->cpuLoadMax)\n    {\n        interpreter->cpuLoadMax = currentCpuLoad;\n    }\n    ++interpreter->cpuLoadTimer;\n    if (interpreter->cpuLoadTimer >= 30)\n    {\n        interpreter->cpuLoadTimer = 0;\n        interpreter->cpuLoadDisplay = interpreter->cpuLoadMax;\n        interpreter->cpuLoadMax = currentCpuLoad;\n    }\n    \n    // reset CPU cycles\n    interpreter->cycles = interpreter->cycles - MAX_CYCLES_TOTAL_PER_FRAME;\n    if (interpreter->cycles < 0)\n    {\n        interpreter->cycles = 0;\n    }\n}\n\nvoid itp_endProgram(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    interpreter->state = StateEnd;\n    interpreter->interruptOverCycles = 0;\n}\n\nvoid itp_freeProgram(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    interpreter->state = StateNoProgram;\n    interpreter->firstData = NULL;\n    interpreter->lastData = NULL;\n    interpreter->currentDataToken = NULL;\n    interpreter->currentDataValueToken = NULL;\n    interpreter->currentOnRasterToken = NULL;\n    interpreter->currentOnVBLToken = NULL;\n    interpreter->lastVariableValue = NULL;\n    \n    var_freeSimpleVariables(interpreter, SUB_LEVEL_GLOBAL);\n    var_freeArrayVariables(interpreter, SUB_LEVEL_GLOBAL);\n    tok_freeTokens(&interpreter->tokenizer);\n    \n    if (interpreter->sourceCode)\n    {\n        free((void *)interpreter->sourceCode);\n        interpreter->sourceCode = NULL;\n    }\n}\n\nenum ValueType itp_getIdentifierTokenValueType(struct Token *token)\n{\n    if (token->type == TokenIdentifier)\n    {\n        return ValueTypeFloat;\n    }\n    else if (token->type == TokenStringIdentifier)\n    {\n        return ValueTypeString;\n    }\n    return ValueTypeNull;\n}\n\nunion Value *itp_readVariable(struct Core *core, enum ValueType *type, enum ErrorCode *errorCode, bool forWriting)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    struct Token *tokenIdentifier = interpreter->pc;\n    \n    if (tokenIdentifier->type != TokenIdentifier && tokenIdentifier->type != TokenStringIdentifier)\n    {\n        *errorCode = ErrorSyntax;\n        return NULL;\n    }\n    \n    enum ValueType varType = itp_getIdentifierTokenValueType(tokenIdentifier);\n    if (type)\n    {\n        *type = varType;\n    }\n    \n    int symbolIndex = tokenIdentifier->symbolIndex;\n    ++interpreter->pc;\n    ++interpreter->cycles;\n    \n    if (interpreter->pc->type == TokenBracketOpen)\n    {\n        // array\n        ++interpreter->pc;\n        \n        struct ArrayVariable *variable = NULL;\n        if (interpreter->pass == PassRun)\n        {\n            variable = var_getArrayVariable(interpreter, symbolIndex, interpreter->subLevel);\n            if (!variable)\n            {\n                *errorCode = ErrorArrayNotDimensionized;\n                return NULL;\n            }\n        }\n        \n        int indices[MAX_ARRAY_DIMENSIONS];\n        int numDimensions = 0;\n        \n        for (int i = 0; i < MAX_ARRAY_DIMENSIONS; i++)\n        {\n            struct TypedValue indexValue = itp_evaluateExpression(core, TypeClassNumeric);\n            if (indexValue.type == ValueTypeError)\n            {\n                *errorCode = indexValue.v.errorCode;\n                return NULL;\n            }\n            \n            numDimensions++;\n            \n            if (interpreter->pass == PassRun)\n            {\n                if (numDimensions <= variable->numDimensions && (indexValue.v.floatValue < 0 || indexValue.v.floatValue >= variable->dimensionSizes[i]))\n                {\n                    *errorCode = ErrorIndexOutOfBounds;\n                    return NULL;\n                }\n                \n                indices[i] = indexValue.v.floatValue;\n            }\n            \n            if (interpreter->pc->type == TokenComma)\n            {\n                ++interpreter->pc;\n            }\n            else\n            {\n                break;\n            }\n        }\n        \n        if (interpreter->pc->type != TokenBracketClose)\n        {\n            *errorCode = ErrorSyntax;\n            return NULL;\n        }\n        ++interpreter->pc;\n        \n        if (interpreter->pass == PassRun)\n        {\n            if (numDimensions != variable->numDimensions)\n            {\n                *errorCode = ErrorWrongNumberOfDimensions;\n                return NULL;\n            }\n            return var_getArrayValue(interpreter, variable, indices);\n        }\n    }\n    else\n    {\n        // simple variable\n        if (interpreter->pass == PassRun)\n        {\n            struct SimpleVariable *variable = var_getSimpleVariable(interpreter, symbolIndex, interpreter->subLevel);\n            if (!variable)\n            {\n                // check if variable name is already used for array\n                if (var_getArrayVariable(interpreter, symbolIndex, interpreter->subLevel))\n                {\n                    *errorCode = ErrorArrayVariableWithoutIndex;\n                    return NULL;\n                }\n                if (!forWriting)\n                {\n                    *errorCode = ErrorVariableNotInitialized;\n                    return NULL;\n                }\n                variable = var_createSimpleVariable(interpreter, errorCode, symbolIndex, interpreter->subLevel, varType, NULL);\n                if (!variable) return NULL;\n            }\n            if (variable->isReference)\n            {\n                return variable->v.reference;\n            }\n            return &variable->v;\n        }\n    }\n    return &ValueDummy;\n}\n\nenum ErrorCode itp_checkTypeClass(struct Interpreter *interpreter, enum ValueType valueType, enum TypeClass typeClass)\n{\n    if (interpreter->pass == PassPrepare && valueType != ValueTypeError)\n    {\n        if (typeClass == TypeClassString && valueType != ValueTypeString)\n        {\n            return ErrorTypeMismatch;\n        }\n        else if (typeClass == TypeClassNumeric && valueType != ValueTypeFloat)\n        {\n            return ErrorTypeMismatch;\n        }\n    }\n    return ErrorNone;\n}\n\nstruct TypedValue itp_evaluateExpression(struct Core *core, enum TypeClass typeClass)\n{\n    struct TypedValue value = itp_evaluateExpressionLevel(core, 0);\n    if (value.type != ValueTypeError)\n    {\n        enum ErrorCode errorCode = itp_checkTypeClass(core->interpreter, value.type, typeClass);\n        if (errorCode != ErrorNone)\n        {\n            value.type = ValueTypeError;\n            value.v.errorCode = errorCode;\n        }\n    }\n    return value;\n}\n\nstruct TypedValue itp_evaluateNumericExpression(struct Core *core, int min, int max)\n{\n    struct TypedValue value = itp_evaluateExpressionLevel(core, 0);\n    if (value.type != ValueTypeError)\n    {\n        enum ErrorCode errorCode = ErrorNone;\n        if (core->interpreter->pass == PassPrepare)\n        {\n            if (value.type != ValueTypeFloat)\n            {\n                errorCode = ErrorTypeMismatch;\n            }\n        }\n        else if (core->interpreter->pass == PassRun)\n        {\n            if ((int)value.v.floatValue < min || (int)value.v.floatValue > max)\n            {\n                errorCode = ErrorInvalidParameter;\n            }\n        }\n        if (errorCode != ErrorNone)\n        {\n            value.type = ValueTypeError;\n            value.v.errorCode = errorCode;\n        }\n    }\n    return value;\n}\n\nstruct TypedValue itp_evaluateOptionalExpression(struct Core *core, enum TypeClass typeClass)\n{\n    if (core->interpreter->pc->type == TokenComma || core->interpreter->pc->type == TokenBracketClose || itp_isEndOfCommand(core->interpreter))\n    {\n        struct TypedValue value;\n        value.type = ValueTypeNull;\n        return value;\n    }\n    return itp_evaluateExpression(core, typeClass);\n}\n\nstruct TypedValue itp_evaluateOptionalNumericExpression(struct Core *core, int min, int max)\n{\n    if (core->interpreter->pc->type == TokenComma || core->interpreter->pc->type == TokenBracketClose || itp_isEndOfCommand(core->interpreter))\n    {\n        struct TypedValue value;\n        value.type = ValueTypeNull;\n        return value;\n    }\n    return itp_evaluateNumericExpression(core, min, max);\n}\n\nbool itp_isTokenLevel(enum TokenType token, int level)\n{\n    switch (level)\n    {\n        case 0:\n            return token == TokenXOR || token == TokenOR;\n        case 1:\n            return token == TokenAND;\n//        case 2:\n//            return token == TokenNOT;\n        case 3:\n            return token == TokenEq || token == TokenUneq || token == TokenGr || token == TokenLe || token == TokenGrEq || token == TokenLeEq;\n        case 4:\n            return token == TokenPlus || token == TokenMinus;\n        case 5:\n            return token == TokenMOD;\n        case 6:\n            return token == TokenMul || token == TokenDiv || token == TokenDivInt;\n//        case 7:\n//            return token == TokenPlus || token == TokenMinus; // unary\n        case 8:\n            return token == TokenPow;\n    }\n    return false;\n}\n\nstruct TypedValue itp_evaluateExpressionLevel(struct Core *core, int level)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    enum TokenType type = interpreter->pc->type;\n    \n    if (level == 2 && type == TokenNOT)\n    {\n        ++interpreter->pc;\n        ++interpreter->cycles;\n        struct TypedValue value = itp_evaluateExpressionLevel(core, level + 1);\n        if (value.type == ValueTypeError) return value;\n        enum ErrorCode errorCode = itp_checkTypeClass(core->interpreter, value.type, TypeClassNumeric);\n        if (errorCode != ErrorNone)\n        {\n            value.type = ValueTypeError;\n            value.v.errorCode = errorCode;\n        }\n        else\n        {\n            value.v.floatValue = ~((int)value.v.floatValue);\n        }\n        interpreter->lastVariableValue = NULL;\n        return value;\n    }\n    if (level == 7 && (type == TokenPlus || type == TokenMinus)) // unary\n    {\n        ++interpreter->pc;\n        ++interpreter->cycles;\n        struct TypedValue value = itp_evaluateExpressionLevel(core, level + 1);\n        if (value.type == ValueTypeError) return value;\n        enum ErrorCode errorCode = itp_checkTypeClass(core->interpreter, value.type, TypeClassNumeric);\n        if (errorCode != ErrorNone)\n        {\n            value.type = ValueTypeError;\n            value.v.errorCode = errorCode;\n        }\n        else if (type == TokenMinus)\n        {\n            value.v.floatValue = -value.v.floatValue;\n        }\n        interpreter->lastVariableValue = NULL;\n        return value;\n    }\n    if (level == 9)\n    {\n        return itp_evaluatePrimaryExpression(core);\n    }\n    \n    struct TypedValue value = itp_evaluateExpressionLevel(core, level + 1);\n    if (value.type == ValueTypeError) return value;\n    \n    while (itp_isTokenLevel(interpreter->pc->type, level))\n    {\n        enum TokenType type = interpreter->pc->type;\n        ++interpreter->pc;\n        ++interpreter->cycles;\n        struct TypedValue rightValue = itp_evaluateExpressionLevel(core, level + 1);\n        if (rightValue.type == ValueTypeError) return rightValue;\n        \n        struct TypedValue newValue;\n        if (value.type != rightValue.type)\n        {\n            newValue.type = ValueTypeError;\n            newValue.v.errorCode = ErrorTypeMismatch;\n            return newValue;\n        }\n        \n        if (value.type == ValueTypeFloat)\n        {\n            newValue.type = ValueTypeFloat;\n            switch (type)\n            {\n                case TokenXOR: {\n                    int leftInt = value.v.floatValue;\n                    int rightInt = rightValue.v.floatValue;\n                    newValue.v.floatValue = (leftInt ^ rightInt);\n                    break;\n                }\n                case TokenOR: {\n                    int leftInt = value.v.floatValue;\n                    int rightInt = rightValue.v.floatValue;\n                    newValue.v.floatValue = (leftInt | rightInt);\n                    break;\n                }\n                case TokenAND: {\n                    int leftInt = value.v.floatValue;\n                    int rightInt = rightValue.v.floatValue;\n                    newValue.v.floatValue = (leftInt & rightInt);\n                    break;\n                }\n                case TokenEq: {\n                    newValue.v.floatValue = (value.v.floatValue == rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE;\n                    break;\n                }\n                case TokenUneq: {\n                    newValue.v.floatValue = (value.v.floatValue != rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE;\n                    break;\n                }\n                case TokenGr: {\n                    newValue.v.floatValue = (value.v.floatValue > rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE;\n                    break;\n                }\n                case TokenLe: {\n                    newValue.v.floatValue = (value.v.floatValue < rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE;\n                    break;\n                }\n                case TokenGrEq: {\n                    newValue.v.floatValue = (value.v.floatValue >= rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE;\n                    break;\n                }\n                case TokenLeEq: {\n                    newValue.v.floatValue = (value.v.floatValue <= rightValue.v.floatValue) ? BAS_TRUE : BAS_FALSE;\n                    break;\n                }\n                case TokenPlus: {\n                    newValue.v.floatValue = value.v.floatValue + rightValue.v.floatValue;\n                    break;\n                }\n                case TokenMinus: {\n                    newValue.v.floatValue = value.v.floatValue - rightValue.v.floatValue;\n                    break;\n                }\n                case TokenMOD: {\n                    if (interpreter->pass == PassRun)\n                    {\n                        int rightInt = (int)rightValue.v.floatValue;\n                        if (rightInt == 0)\n                        {\n                            newValue.type = ValueTypeError;\n                            newValue.v.errorCode = ErrorDivisionByZero;\n                        }\n                        else\n                        {\n                            newValue.v.floatValue = (int)value.v.floatValue % rightInt;\n                        }\n                    }\n                    break;\n                }\n                case TokenMul: {\n                    newValue.v.floatValue = value.v.floatValue * rightValue.v.floatValue;\n                    break;\n                }\n                case TokenDiv: {\n                    if (interpreter->pass == PassRun)\n                    {\n                        if (rightValue.v.floatValue == 0.0f)\n                        {\n                            newValue.type = ValueTypeError;\n                            newValue.v.errorCode = ErrorDivisionByZero;\n                        }\n                        else\n                        {\n                            newValue.v.floatValue = value.v.floatValue / rightValue.v.floatValue;\n                        }\n                    }\n                    break;\n                }\n                case TokenDivInt: {\n                    if (interpreter->pass == PassRun)\n                    {\n                        int rightInt = (int)rightValue.v.floatValue;\n                        if (rightInt == 0)\n                        {\n                            newValue.type = ValueTypeError;\n                            newValue.v.errorCode = ErrorDivisionByZero;\n                        }\n                        else\n                        {\n                            newValue.v.floatValue = (int)value.v.floatValue / rightInt;\n                        }\n                    }\n                    break;\n                }\n                case TokenPow: {\n                    newValue.v.floatValue = powf(value.v.floatValue, rightValue.v.floatValue);\n                    break;\n                }\n                default: {\n                    newValue.type = ValueTypeError;\n                    newValue.v.errorCode = ErrorSyntax;\n                }\n            }\n        }\n        else if (value.type == ValueTypeString)\n        {\n            switch (type)\n            {\n                case TokenEq: {\n                    newValue.type = ValueTypeFloat;\n                    if (interpreter->pass == PassRun)\n                    {\n                        newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) == 0) ? BAS_TRUE : BAS_FALSE;\n                    }\n                    break;\n                }\n                case TokenUneq: {\n                    newValue.type = ValueTypeFloat;\n                    if (interpreter->pass == PassRun)\n                    {\n                        newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) != 0) ? BAS_TRUE : BAS_FALSE;\n                    }\n                    break;\n                }\n                case TokenGr: {\n                    newValue.type = ValueTypeFloat;\n                    if (interpreter->pass == PassRun)\n                    {\n                        newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) > 0) ? BAS_TRUE : BAS_FALSE;\n                    }\n                    break;\n                }\n                case TokenLe: {\n                    newValue.type = ValueTypeFloat;\n                    if (interpreter->pass == PassRun)\n                    {\n                        newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) < 0) ? BAS_TRUE : BAS_FALSE;\n                    }\n                    break;\n                }\n                case TokenGrEq: {\n                    newValue.type = ValueTypeFloat;\n                    if (interpreter->pass == PassRun)\n                    {\n                        newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) >= 0) ? BAS_TRUE : BAS_FALSE;\n                    }\n                    break;\n                }\n                case TokenLeEq: {\n                    newValue.type = ValueTypeFloat;\n                    if (interpreter->pass == PassRun)\n                    {\n                        newValue.v.floatValue = (strcmp(value.v.stringValue->chars, rightValue.v.stringValue->chars) <= 0) ? BAS_TRUE : BAS_FALSE;\n                    }\n                    break;\n                }\n                case TokenPlus: {\n                    newValue.type = ValueTypeString;\n                    if (interpreter->pass == PassRun)\n                    {\n                        size_t len1 = strlen(value.v.stringValue->chars);\n                        size_t len2 = strlen(rightValue.v.stringValue->chars);\n                        newValue.v.stringValue = rcstring_new(NULL, len1 + len2);\n                        strcpy(newValue.v.stringValue->chars, value.v.stringValue->chars);\n                        strcpy(&newValue.v.stringValue->chars[len1], rightValue.v.stringValue->chars);\n                        interpreter->cycles += len1 + len2;\n                    }\n                    break;\n                }\n                case TokenXOR:\n                case TokenOR:\n                case TokenAND:\n                case TokenMinus:\n                case TokenMOD:\n                case TokenMul:\n                case TokenDiv:\n                case TokenDivInt:\n                case TokenPow: {\n                    newValue.type = ValueTypeError;\n                    newValue.v.errorCode = ErrorTypeMismatch;\n                    break;\n                }\n                default: {\n                    newValue.type = ValueTypeError;\n                    newValue.v.errorCode = ErrorSyntax;\n                }\n            }\n            if (interpreter->pass == PassRun)\n            {\n                rcstring_release(value.v.stringValue);\n                rcstring_release(rightValue.v.stringValue);\n            }\n        }\n        else\n        {\n            assert(0);\n            newValue.v.floatValue = 0;\n        }\n        \n        value = newValue;\n        interpreter->lastVariableValue = NULL;\n        if (value.type == ValueTypeError) break;\n    }\n    return value;\n}\n\nstruct TypedValue itp_evaluatePrimaryExpression(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    // check for function\n    struct TypedValue value = itp_evaluateFunction(core);\n    if (value.type != ValueTypeNull)\n    {\n        ++interpreter->cycles;\n        interpreter->lastVariableValue = NULL;\n        return value;\n    }\n    \n    interpreter->lastVariableValue = NULL;\n    \n    // native types\n    switch (interpreter->pc->type)\n    {\n        case TokenFloat: {\n            value.type = ValueTypeFloat;\n            value.v.floatValue = interpreter->pc->floatValue;\n            ++interpreter->pc;\n            ++interpreter->cycles;\n            break;\n        }\n        case TokenString: {\n            value.type = ValueTypeString;\n            value.v.stringValue = interpreter->pc->stringValue;\n            if (interpreter->pass == PassRun)\n            {\n                rcstring_retain(interpreter->pc->stringValue);\n            }\n            ++interpreter->pc;\n            ++interpreter->cycles;\n            break;\n        }\n        case TokenIdentifier:\n        case TokenStringIdentifier: {\n            enum ErrorCode errorCode = ErrorNone;\n            enum ValueType valueType = ValueTypeNull;\n            union Value *varValue = itp_readVariable(core, &valueType, &errorCode, false);\n            if (varValue)\n            {\n                value.type = valueType;\n                value.v = *varValue;\n                interpreter->lastVariableValue = varValue;\n                if (interpreter->pass == PassRun && valueType == ValueTypeString)\n                {\n                    rcstring_retain(varValue->stringValue);\n                }\n            }\n            else\n            {\n                value.type = ValueTypeError;\n                value.v.errorCode = errorCode;\n            }\n            break;\n        }\n        case TokenBracketOpen: {\n            ++interpreter->pc;\n            value = itp_evaluateExpression(core, TypeClassAny);\n            if (value.type == ValueTypeError) return value;\n            if (interpreter->pc->type != TokenBracketClose)\n            {\n                value.type = ValueTypeError;\n                value.v.errorCode = ErrorSyntax;\n            }\n            else\n            {\n                ++interpreter->pc;\n                interpreter->lastVariableValue = NULL;\n            }\n            break;\n        }\n        default: {\n            value.type = ValueTypeError;\n            value.v.errorCode = ErrorSyntax;\n        }\n    }\n    return value;\n}\n\nbool itp_isEndOfCommand(struct Interpreter *interpreter)\n{\n    enum TokenType type = interpreter->pc->type;\n    return (type == TokenEol || type == TokenELSE);\n}\n\nenum ErrorCode itp_endOfCommand(struct Interpreter *interpreter)\n{\n    enum TokenType type = interpreter->pc->type;\n    if (type == TokenEol)\n    {\n        interpreter->isSingleLineIf = false;\n        ++interpreter->pc;\n        return ErrorNone;\n    }\n    return (type == TokenELSE) ? ErrorNone : ErrorSyntax;\n}\n\nenum TokenType itp_getNextTokenType(struct Interpreter *interpreter)\n{\n    return (interpreter->pc + 1)->type;\n}\n\nstruct TypedValue itp_evaluateFunction(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    switch (interpreter->pc->type)\n    {\n        case TokenASC:\n            return fnc_ASC(core);\n            \n        case TokenBIN:\n        case TokenHEX:\n            return fnc_BIN_HEX(core);\n            \n        case TokenCHR:\n            return fnc_CHR(core);\n            \n        case TokenINSTR:\n            return fnc_INSTR(core);\n            \n        case TokenLEFTStr:\n        case TokenRIGHTStr:\n            return fnc_LEFTStr_RIGHTStr(core);\n            \n        case TokenLEN:\n            return fnc_LEN(core);\n            \n        case TokenMID:\n            return fnc_MID(core);\n            \n        case TokenSTR:\n            return fnc_STR(core);\n            \n        case TokenVAL:\n            return fnc_VAL(core);\n\n        case TokenPEEK:\n        case TokenPEEKW:\n        case TokenPEEKL:\n            return fnc_PEEK(core);\n            \n        case TokenPI:\n            return fnc_math0(core);\n\n        case TokenABS:\n        case TokenACOS:\n        case TokenASIN:\n        case TokenATAN:\n        case TokenCOS:\n        case TokenEXP:\n        case TokenHCOS:\n        case TokenHSIN:\n        case TokenHTAN:\n        case TokenINT:\n        case TokenLOG:\n        case TokenSGN:\n        case TokenSIN:\n        case TokenSQR:\n        case TokenTAN:\n            return fnc_math1(core);\n\n        case TokenMAX:\n        case TokenMIN:\n            return fnc_math2(core);\n            \n        case TokenRND:\n            return fnc_RND(core);\n            \n        case TokenINKEY:\n            return fnc_INKEY(core);\n            \n        case TokenUBOUND:\n            return fnc_UBOUND(core);\n            \n        case TokenROM:\n        case TokenSIZE:\n            return fnc_ROM_SIZE(core);\n            \n        case TokenCOLOR:\n            return fnc_COLOR(core);\n            \n        case TokenTIMER:\n        case TokenRASTER:\n        case TokenDISPLAY:\n            return fnc_screen0(core);\n            \n        case TokenSCROLLX:\n        case TokenSCROLLY:\n            return fnc_SCROLL_X_Y(core);\n            \n        case TokenCELLA:\n        case TokenCELLC:\n            return fnc_CELL(core);\n            \n        case TokenMCELLA:\n        case TokenMCELLC:\n            return fnc_MCELL(core);\n            \n        case TokenCURSORX:\n        case TokenCURSORY:\n            return fnc_CURSOR(core);\n            \n        case TokenUP:\n        case TokenDOWN:\n        case TokenLEFT:\n        case TokenRIGHT:\n            return fnc_UP_DOWN_LEFT_RIGHT(core);\n            \n        case TokenBUTTON:\n            return fnc_BUTTON(core);\n            \n        case TokenSPRITEX:\n        case TokenSPRITEY:\n        case TokenSPRITEC:\n        case TokenSPRITEA:\n            return fnc_SPRITE(core);\n            \n        case TokenSPRITE:\n            return fnc_SPRITE_HIT(core);\n            \n        case TokenHIT:\n            return fnc_HIT(core);\n            \n        case TokenTOUCH:\n            return fnc_TOUCH(core);\n\n        case TokenTAP:\n            return fnc_TAP(core);\n            \n        case TokenTOUCHX:\n        case TokenTOUCHY:\n            return fnc_TOUCH_X_Y(core);\n            \n        case TokenFILE:\n            return fnc_FILE(core);\n            \n        case TokenFSIZE:\n            return fnc_FSIZE(core);\n            \n        case TokenPAUSE:\n            return fnc_PAUSE(core);\n            \n        case TokenMUSIC:\n            return fnc_MUSIC(core);\n            \n        default:\n            break;\n    }\n    struct TypedValue value;\n    value.type = ValueTypeNull;\n    return value;\n}\n\nenum ErrorCode itp_evaluateCommand(struct Core *core)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    enum TokenType type = interpreter->pc->type;\n    if (type != TokenREM && type != TokenApostrophe && type != TokenEol && type != TokenUndefined)\n    {\n        ++interpreter->cycles;\n    }\n    switch (type)\n    {\n        case TokenUndefined:\n            if (interpreter->pass == PassRun)\n            {\n                itp_endProgram(core);\n            }\n            break;\n            \n        case TokenREM:\n        case TokenApostrophe:\n            ++interpreter->pc;\n            break;\n            \n        case TokenLabel:\n            ++interpreter->pc;\n            if (interpreter->pc->type != TokenEol) return ErrorSyntax;\n            ++interpreter->pc;\n            break;\n        \n        case TokenEol:\n            interpreter->isSingleLineIf = false;\n            ++interpreter->pc;\n            break;\n            \n        case TokenEND:\n            switch (itp_getNextTokenType(interpreter))\n            {\n                case TokenIF:\n                    return cmd_END_IF(core);\n                    \n                case TokenSUB:\n                    return cmd_END_SUB(core);\n                    \n                default:\n                    return cmd_END(core);\n            }\n            break;\n            \n        case TokenLET:\n        case TokenIdentifier:\n        case TokenStringIdentifier:\n            return cmd_LET(core);\n            \n        case TokenDIM:\n            return cmd_DIM(core);\n        \n        case TokenPRINT:\n            return cmd_PRINT(core);\n            \n        case TokenCLS:\n            return cmd_CLS(core);\n            \n        case TokenINPUT:\n            return cmd_INPUT(core);\n        \n        case TokenIF:\n            return cmd_IF(core, false);\n        \n        case TokenELSE:\n            return cmd_ELSE(core);\n\n        case TokenFOR:\n            return cmd_FOR(core);\n\n        case TokenNEXT:\n            return cmd_NEXT(core);\n\n        case TokenGOTO:\n            return cmd_GOTO(core);\n\n        case TokenGOSUB:\n            return cmd_GOSUB(core);\n            \n        case TokenRETURN:\n            return cmd_RETURN(core);\n            \n        case TokenDATA:\n            return cmd_DATA(core);\n\n        case TokenREAD:\n            return cmd_READ(core);\n\n        case TokenRESTORE:\n            return cmd_RESTORE(core);\n\n        case TokenPOKE:\n        case TokenPOKEW:\n        case TokenPOKEL:\n            return cmd_POKE(core);\n            \n        case TokenFILL:\n            return cmd_FILL(core);\n            \n        case TokenCOPY:\n            return cmd_COPY(core);\n            \n        case TokenROL:\n        case TokenROR:\n            return cmd_ROL_ROR(core);\n            \n        case TokenWAIT:\n            return cmd_WAIT(core);\n            \n        case TokenON:\n            return cmd_ON(core);\n            \n        case TokenSWAP:\n            return cmd_SWAP(core);\n            \n        case TokenTEXT:\n            return cmd_TEXT(core);\n\n        case TokenNUMBER:\n            return cmd_NUMBER(core);\n            \n        case TokenDO:\n            return cmd_DO(core);\n            \n        case TokenLOOP:\n            return cmd_LOOP(core);\n        \n        case TokenREPEAT:\n            return cmd_REPEAT(core);\n            \n        case TokenUNTIL:\n            return cmd_UNTIL(core);\n            \n        case TokenWHILE:\n            return cmd_WHILE(core);\n            \n        case TokenWEND:\n            return cmd_WEND(core);\n            \n        case TokenSYSTEM:\n            return cmd_SYSTEM(core);\n\n        case TokenRANDOMIZE:\n            return cmd_RANDOMIZE(core);\n            \n        case TokenADD:\n            return cmd_ADD(core);\n            \n        case TokenINC:\n        case TokenDEC:\n            return cmd_INC_DEC(core);\n            \n        case TokenLEFTStr:\n        case TokenRIGHTStr:\n            return cmd_LEFT_RIGHT(core);\n            \n        case TokenMID:\n            return cmd_MID(core);\n            \n        case TokenWINDOW:\n            return cmd_WINDOW(core);\n            \n        case TokenFONT:\n            return cmd_FONT(core);\n            \n        case TokenLOCATE:\n            return cmd_LOCATE(core);\n            \n        case TokenCLW:\n            return cmd_CLW(core);\n            \n        case TokenBG:\n            switch (itp_getNextTokenType(interpreter))\n            {\n                case TokenSOURCE:\n                    return cmd_BG_SOURCE(core);\n                    \n                case TokenCOPY:\n                    return cmd_BG_COPY(core);\n                    \n                case TokenSCROLL:\n                    return cmd_BG_SCROLL(core);\n                    \n                case TokenFILL:\n                    return cmd_BG_FILL(core);\n                    \n                case TokenTINT:\n                    return cmd_BG_TINT(core);\n                    \n                case TokenVIEW:\n                    return cmd_BG_VIEW(core);\n                    \n                default:\n                    return cmd_BG(core);\n            }\n            break;\n            \n        case TokenATTR:\n            return cmd_ATTR(core);\n            \n        case TokenPAL:\n            return cmd_PAL(core);\n            \n        case TokenFLIP:\n            return cmd_FLIP(core);\n            \n        case TokenPRIO:\n            return cmd_PRIO(core);\n            \n        case TokenCELL:\n            switch (itp_getNextTokenType(interpreter))\n            {\n                case TokenSIZE:\n                    return cmd_CELL_SIZE(core);\n                    \n                default:\n                    return cmd_CELL(core);\n            }\n            break;\n            \n        case TokenTINT:\n            return cmd_TINT(core);\n            \n        case TokenMCELL:\n            return cmd_MCELL(core);\n            \n        case TokenPALETTE:\n            return cmd_PALETTE(core);\n            \n        case TokenSCROLL:\n            return cmd_SCROLL(core);\n\n        case TokenDISPLAY:\n            return cmd_DISPLAY(core);\n            \n        case TokenSPRITEA:\n            return cmd_SPRITE_A(core);\n            \n        case TokenSPRITE:\n            switch (itp_getNextTokenType(interpreter))\n            {\n                case TokenOFF:\n                    return cmd_SPRITE_OFF(core);\n                    \n                case TokenVIEW:\n                    return cmd_SPRITE_VIEW(core);\n                    \n                default:\n                    return cmd_SPRITE(core);\n            }\n            break;\n            \n        case TokenSAVE:\n            return cmd_SAVE(core);\n            \n        case TokenLOAD:\n            return cmd_LOAD(core);\n            \n        case TokenFILES:\n            return cmd_FILES(core);\n            \n        case TokenGAMEPAD:\n            return cmd_GAMEPAD(core);\n            \n        case TokenKEYBOARD:\n            return cmd_KEYBOARD(core);\n            \n        case TokenTOUCHSCREEN:\n            return cmd_TOUCHSCREEN(core);\n            \n        case TokenTRACE:\n            return cmd_TRACE(core);\n            \n        case TokenCALL:\n            return cmd_CALL(core);\n            \n        case TokenSUB:\n            return cmd_SUB(core);\n            \n//        case TokenSHARED:\n//            return cmd_SHARED(core);\n            \n        case TokenGLOBAL:\n            return cmd_GLOBAL(core);\n            \n        case TokenEXIT:\n            switch (itp_getNextTokenType(interpreter))\n            {\n                case TokenSUB:\n                    return cmd_EXIT_SUB(core);\n                default:\n                    return cmd_EXIT(core);\n            }\n            break;\n            \n        case TokenPAUSE:\n            return cmd_PAUSE(core);\n            \n        case TokenSOUND:\n            switch (itp_getNextTokenType(interpreter))\n            {\n//                case TokenCOPY:\n//                    return cmd_SOUND_COPY(core);\n                    \n                case TokenSOURCE:\n                    return cmd_SOUND_SOURCE(core);\n                    \n                default:\n                    return cmd_SOUND(core);\n            }\n            break;\n            \n        case TokenVOLUME:\n            return cmd_VOLUME(core);\n            \n        case TokenENVELOPE:\n            return cmd_ENVELOPE(core);\n            \n        case TokenLFO:\n            switch (itp_getNextTokenType(interpreter))\n            {\n                case TokenWAVE:\n                    return cmd_LFO_WAVE(core);\n                    \n                default:\n                    return cmd_LFO(core);\n            }\n            break;\n            \n        case TokenLFOA:\n            return cmd_LFO_A(core);\n            \n        case TokenPLAY:\n            return cmd_PLAY(core);\n            \n        case TokenSTOP:\n            return cmd_STOP(core);\n            \n        case TokenMUSIC:\n            return cmd_MUSIC(core);\n            \n        case TokenTRACK:\n            return cmd_TRACK(core);\n            \n        default:\n            printf(\"Command not implemented: %s\\n\", TokenStrings[interpreter->pc->type]);\n            return ErrorSyntax;\n    }\n    return ErrorNone;\n}\n\nenum ErrorCode itp_labelStackError(struct LabelStackItem *item)\n{\n    switch (item->type)\n    {\n        case LabelTypeIF:\n        case LabelTypeELSEIF:\n        case LabelTypeELSE:\n            return ErrorIfWithoutEndIf;\n            \n        case LabelTypeFOR:\n            return  ErrorForWithoutNext;\n            \n        case LabelTypeDO:\n            return ErrorDoWithoutLoop;\n            \n        case LabelTypeREPEAT:\n            return ErrorRepeatWithoutUntil;\n            \n        case LabelTypeWHILE:\n            return ErrorWhileWithoutWend;\n            \n        case LabelTypeSUB:\n            return ErrorSubWithoutEndSub;\n            \n        case LabelTypeFORVar:\n        case LabelTypeFORLimit:\n        case LabelTypeGOSUB:\n        case LabelTypeCALL:\n        case LabelTypeONCALL:\n            // should not happen in compile time\n            return ErrorSyntax;\n    }\n}\n"
  },
  {
    "path": "core/interpreter/interpreter.h",
    "content": "//\n// Copyright 2016-2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef interpreter_h\n#define interpreter_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"interpreter_config.h\"\n#include \"tokenizer.h\"\n#include \"token.h\"\n#include \"error.h\"\n#include \"value.h\"\n#include \"labels.h\"\n#include \"variables.h\"\n#include \"data.h\"\n#include \"text_lib.h\"\n#include \"sprites_lib.h\"\n#include \"audio_lib.h\"\n#include \"io_chip.h\"\n#include \"data_manager.h\"\n\n#define BAS_TRUE -1.0f\n#define BAS_FALSE 0.0f\n\nstruct Core;\n\nenum Pass {\n    PassPrepare,\n    PassRun\n};\n\nenum State {\n    StateNoProgram,\n    StateEvaluate,\n    StateInput,\n    StatePaused,\n    StateWaitForDisk,\n    StateEnd\n};\n\nenum Mode {\n    ModeNone,\n    ModeMain,\n    ModeInterrupt\n};\n\nenum InterruptType {\n    InterruptTypeRaster,\n    InterruptTypeVBL\n};\n\nstruct Interpreter {\n    const char *sourceCode;\n    \n    enum Pass pass;\n    enum State state;\n    enum Mode mode;\n    struct Token *pc;\n    int subLevel;\n    int cycles;\n    int interruptOverCycles;\n    bool debug;\n    bool handlesPause;\n    int cpuLoadDisplay;\n    int cpuLoadMax;\n    int cpuLoadTimer;\n    \n    struct Tokenizer tokenizer;\n    \n    struct DataManager romDataManager;\n    \n    struct LabelStackItem labelStackItems[MAX_LABEL_STACK_ITEMS];\n    int numLabelStackItems;\n    \n    bool isSingleLineIf;\n    \n    struct SimpleVariable simpleVariables[MAX_SIMPLE_VARIABLES];\n    int numSimpleVariables;\n    struct ArrayVariable arrayVariables[MAX_ARRAY_VARIABLES];\n    int numArrayVariables;\n    struct RCString *nullString;\n    \n    struct Token *firstData;\n    struct Token *lastData;\n    struct Token *currentDataToken;\n    struct Token *currentDataValueToken;\n    \n    struct Token *currentOnRasterToken;\n    struct Token *currentOnVBLToken;\n    \n    int waitCount;\n    bool exitEvaluation;\n    union Gamepad lastFrameGamepads[NUM_GAMEPADS];\n    union IOStatus lastFrameIOStatus;\n    float timer;\n    int seed;\n    bool isKeyboardOptional;\n    union Value *lastVariableValue;\n    \n    struct TextLib textLib;\n    struct SpritesLib spritesLib;\n    struct AudioLib audioLib;\n};\n\nvoid itp_init(struct Core *core);\nvoid itp_deinit(struct Core *core);\nstruct CoreError itp_compileProgram(struct Core *core, const char *sourceCode);\nvoid itp_runProgram(struct Core *core);\nvoid itp_runInterrupt(struct Core *core, enum InterruptType type);\nvoid itp_didFinishVBL(struct Core *core);\nvoid itp_endProgram(struct Core *core);\nvoid itp_freeProgram(struct Core *core);\n\nenum ValueType itp_getIdentifierTokenValueType(struct Token *token);\nunion Value *itp_readVariable(struct Core *core, enum ValueType *type, enum ErrorCode *errorCode, bool forWriting);\nstruct TypedValue itp_evaluateExpression(struct Core *core, enum TypeClass typeClass);\nstruct TypedValue itp_evaluateNumericExpression(struct Core *core, int min, int max);\nstruct TypedValue itp_evaluateOptionalExpression(struct Core *core, enum TypeClass typeClass);\nstruct TypedValue itp_evaluateOptionalNumericExpression(struct Core *core, int min, int max);\nbool itp_isEndOfCommand(struct Interpreter *interpreter);\nenum ErrorCode itp_endOfCommand(struct Interpreter *interpreter);\nenum ErrorCode itp_labelStackError(struct LabelStackItem *item);\n\n#endif /* interpreter_h */\n"
  },
  {
    "path": "core/interpreter/interpreter_config.h",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef interpreter_config_h\n#define interpreter_config_h\n\n#define MAX_TOKENS 16384\n#define MAX_SYMBOLS 2048\n#define MAX_LABEL_STACK_ITEMS 128\n#define MAX_JUMP_LABEL_ITEMS 256\n#define MAX_SUB_ITEMS 256\n#define MAX_SIMPLE_VARIABLES 256\n#define MAX_ARRAY_VARIABLES 256\n#define SYMBOL_NAME_SIZE 21\n#define MAX_ARRAY_DIMENSIONS 4\n#define MAX_ARRAY_SIZE 32768\n#define MAX_CYCLES_TOTAL_PER_FRAME 17556\n#define MAX_CYCLES_PER_VBL 1140\n#define MAX_CYCLES_PER_RASTER 51\n#define TIMER_WRAP_VALUE 5184000\n\n#endif /* interpreter_config_h */\n"
  },
  {
    "path": "core/interpreter/interpreter_utils.c",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"interpreter_utils.h\"\n#include \"core.h\"\n\nenum ErrorCode itp_evaluateSimpleAttributes(struct Core *core, struct SimpleAttributes *attrs)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    \n    attrs->pal = -1;\n    attrs->flipX = -1;\n    attrs->flipY = -1;\n    attrs->prio = -1;\n    attrs->size = -1;\n    \n    bool changed = false;\n    bool checked = false;\n    \n    do\n    {\n        checked = false;\n        \n        // PAL\n        if (interpreter->pc->type == TokenPAL && attrs->pal == -1)\n        {\n            ++interpreter->pc;\n            \n            struct TypedValue value = itp_evaluateNumericExpression(core, 0, NUM_PALETTES - 1);\n            if (value.type == ValueTypeError) return value.v.errorCode;\n            attrs->pal = value.v.floatValue;\n            \n            checked = true;\n        }\n        \n        // FLIP\n        if (interpreter->pc->type == TokenFLIP && attrs->flipX == -1)\n        {\n            ++interpreter->pc;\n            \n            struct TypedValue fxValue = itp_evaluateNumericExpression(core, -1, 1);\n            if (fxValue.type == ValueTypeError) return fxValue.v.errorCode;\n            attrs->flipX = fxValue.v.floatValue ? 1 : 0;\n            \n            // comma\n            if (interpreter->pc->type != TokenComma) return ErrorSyntax;\n            ++interpreter->pc;\n            \n            struct TypedValue fyValue = itp_evaluateNumericExpression(core, -1, 1);\n            if (fyValue.type == ValueTypeError) return fyValue.v.errorCode;\n            attrs->flipY = fyValue.v.floatValue ? 1 : 0;\n            \n            checked = true;\n        }\n        \n        // PRIO\n        if (interpreter->pc->type == TokenPRIO && attrs->prio == -1)\n        {\n            ++interpreter->pc;\n            \n            struct TypedValue value = itp_evaluateNumericExpression(core, -1, 1);\n            if (value.type == ValueTypeError) return value.v.errorCode;\n            attrs->prio = value.v.floatValue ? 1 : 0;\n            \n            checked = true;\n        }\n        \n        // SIZE\n        if (interpreter->pc->type == TokenSIZE && attrs->size == -1)\n        {\n            ++interpreter->pc;\n            \n            struct TypedValue value = itp_evaluateNumericExpression(core, 0, 3);\n            if (value.type == ValueTypeError) return value.v.errorCode;\n            attrs->size = value.v.floatValue;\n            \n            checked = true;\n        }\n        \n        changed |= checked;\n    }\n    while (checked);\n    \n    if (!changed) return ErrorSyntax;\n    \n    return ErrorNone;\n}\n\nstruct TypedValue itp_evaluateCharAttributes(struct Core *core, union CharacterAttributes oldAttr)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pc->type == TokenBracketOpen)\n    {\n        // bracket open\n        interpreter->pc++;\n        \n        // obsolete syntax!\n        \n        union CharacterAttributes resultAttr = oldAttr;\n        \n        struct TypedValue palValue = {ValueTypeNull, 0};\n        struct TypedValue fxValue = {ValueTypeNull, 0};\n        struct TypedValue fyValue = {ValueTypeNull, 0};\n        struct TypedValue priValue = {ValueTypeNull, 0};\n        struct TypedValue sValue = {ValueTypeNull, 0};\n        \n        // palette value\n        palValue = itp_evaluateOptionalNumericExpression(core, 0, NUM_PALETTES - 1);\n        if (palValue.type == ValueTypeError) return palValue;\n        \n        // comma\n        if (interpreter->pc->type == TokenComma)\n        {\n            ++interpreter->pc;\n            \n            // flip x value\n            fxValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n            if (fxValue.type == ValueTypeError) return fxValue;\n            \n            // comma\n            if (interpreter->pc->type == TokenComma)\n            {\n                ++interpreter->pc;\n                \n                // flip y value\n                fyValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n                if (fyValue.type == ValueTypeError) return fyValue;\n                \n                // comma\n                if (interpreter->pc->type == TokenComma)\n                {\n                    ++interpreter->pc;\n                    \n                    // priority value\n                    priValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n                    if (priValue.type == ValueTypeError) return priValue;\n\n                    // comma\n                    if (interpreter->pc->type == TokenComma)\n                    {\n                        ++interpreter->pc;\n                        \n                        // size value\n                        sValue = itp_evaluateOptionalNumericExpression(core, 0, 3);\n                        if (sValue.type == ValueTypeError) return sValue;\n                    }\n                }\n            }\n        }\n        \n        // bracket close\n        if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n        interpreter->pc++;\n        \n        if (interpreter->pass == PassRun)\n        {\n            if (palValue.type != ValueTypeNull) resultAttr.palette = palValue.v.floatValue;\n            if (fxValue.type != ValueTypeNull) resultAttr.flipX = fxValue.v.floatValue;\n            if (fyValue.type != ValueTypeNull) resultAttr.flipY = fyValue.v.floatValue;\n            if (priValue.type != ValueTypeNull) resultAttr.priority = priValue.v.floatValue;\n            if (sValue.type != ValueTypeNull) resultAttr.size = sValue.v.floatValue;\n        }\n        \n        struct TypedValue resultValue;\n        resultValue.type = ValueTypeFloat;\n        resultValue.v.floatValue = resultAttr.value;\n        return resultValue;\n    }\n    else\n    {\n        return itp_evaluateNumericExpression(core, 0, 255);\n    }\n}\n\nstruct TypedValue itp_evaluateDisplayAttributes(struct Core *core, union DisplayAttributes oldAttr)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pc->type == TokenBracketOpen)\n    {\n        // bracket open\n        interpreter->pc++;\n        \n        union DisplayAttributes resultAttr = oldAttr;\n        \n        struct TypedValue sValue = {ValueTypeNull, 0};\n        struct TypedValue bg0Value = {ValueTypeNull, 0};\n        struct TypedValue bg1Value = {ValueTypeNull, 0};\n        struct TypedValue bg0SizeValue = {ValueTypeNull, 0};\n        struct TypedValue bg1SizeValue = {ValueTypeNull, 0};\n\n        // sprites value\n        sValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n        if (sValue.type == ValueTypeError) return sValue;\n\n        // comma\n        if (interpreter->pc->type == TokenComma)\n        {\n            ++interpreter->pc;\n            \n            // bg0 value\n            bg0Value = itp_evaluateOptionalNumericExpression(core, -1, 1);\n            if (bg0Value.type == ValueTypeError) return bg0Value;\n\n            // comma\n            if (interpreter->pc->type == TokenComma)\n            {\n                ++interpreter->pc;\n                \n                // bg1 value\n                bg1Value = itp_evaluateOptionalNumericExpression(core, -1, 1);\n                if (bg1Value.type == ValueTypeError) return bg1Value;\n                \n                // comma\n                if (interpreter->pc->type == TokenComma)\n                {\n                    ++interpreter->pc;\n                    \n                    // bg0 cell size value\n                    bg0SizeValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n                    if (bg0SizeValue.type == ValueTypeError) return bg0SizeValue;\n                    \n                    // comma\n                    if (interpreter->pc->type == TokenComma)\n                    {\n                        ++interpreter->pc;\n                        \n                        // bg1 cell size value\n                        bg1SizeValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n                        if (bg1SizeValue.type == ValueTypeError) return bg1SizeValue;\n                    }\n                }\n            }\n        }\n        \n        // bracket close\n        if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n        interpreter->pc++;\n        \n        if (interpreter->pass == PassRun)\n        {\n            if (sValue.type != ValueTypeNull) resultAttr.spritesEnabled = sValue.v.floatValue;\n            if (bg0Value.type != ValueTypeNull) resultAttr.planeAEnabled = bg0Value.v.floatValue;\n            if (bg1Value.type != ValueTypeNull) resultAttr.planeBEnabled = bg1Value.v.floatValue;\n            if (bg0SizeValue.type != ValueTypeNull) resultAttr.planeACellSize = bg0SizeValue.v.floatValue;\n            if (bg1SizeValue.type != ValueTypeNull) resultAttr.planeBCellSize = bg1SizeValue.v.floatValue;\n        }\n        \n        struct TypedValue resultValue;\n        resultValue.type = ValueTypeFloat;\n        resultValue.v.floatValue = resultAttr.value;\n        return resultValue;\n    }\n    else\n    {\n        return itp_evaluateNumericExpression(core, 0, 255);\n    }\n}\n\nstruct TypedValue itp_evaluateLFOAttributes(struct Core *core, union LFOAttributes oldAttr)\n{\n    struct Interpreter *interpreter = core->interpreter;\n    if (interpreter->pc->type == TokenBracketOpen)\n    {\n        // bracket open\n        interpreter->pc++;\n        \n        union LFOAttributes resultAttr = oldAttr;\n        \n        struct TypedValue wavValue = {ValueTypeNull, 0};\n        struct TypedValue invValue = {ValueTypeNull, 0};\n        struct TypedValue envValue = {ValueTypeNull, 0};\n        struct TypedValue triValue = {ValueTypeNull, 0};\n        \n        // wave value\n        wavValue = itp_evaluateOptionalNumericExpression(core, 0, 3);\n        if (wavValue.type == ValueTypeError) return wavValue;\n        \n        // comma\n        if (interpreter->pc->type == TokenComma)\n        {\n            ++interpreter->pc;\n            \n            // invert value\n            invValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n            if (invValue.type == ValueTypeError) return invValue;\n            \n            // comma\n            if (interpreter->pc->type == TokenComma)\n            {\n                ++interpreter->pc;\n                \n                // env mode value\n                envValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n                if (envValue.type == ValueTypeError) return envValue;\n                \n                // comma\n                if (interpreter->pc->type == TokenComma)\n                {\n                    ++interpreter->pc;\n                    \n                    // trigger value\n                    triValue = itp_evaluateOptionalNumericExpression(core, -1, 1);\n                    if (triValue.type == ValueTypeError) return triValue;\n                }\n            }\n        }\n        \n        // bracket close\n        if (interpreter->pc->type != TokenBracketClose) return val_makeError(ErrorSyntax);\n        interpreter->pc++;\n        \n        if (interpreter->pass == PassRun)\n        {\n            if (wavValue.type != ValueTypeNull) resultAttr.wave = wavValue.v.floatValue;\n            if (invValue.type != ValueTypeNull) resultAttr.invert = invValue.v.floatValue;\n            if (envValue.type != ValueTypeNull) resultAttr.envMode = envValue.v.floatValue;\n            if (triValue.type != ValueTypeNull) resultAttr.trigger = triValue.v.floatValue;\n        }\n        \n        struct TypedValue resultValue;\n        resultValue.type = ValueTypeFloat;\n        resultValue.v.floatValue = resultAttr.value;\n        return resultValue;\n    }\n    else\n    {\n        return itp_evaluateNumericExpression(core, 0, 255);\n    }\n}\n\n"
  },
  {
    "path": "core/interpreter/interpreter_utils.h",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef interpreter_utils_h\n#define interpreter_utils_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"value.h\"\n#include \"video_chip.h\"\n#include \"audio_chip.h\"\n\nstruct Core;\n\nstruct SimpleAttributes\n{\n    int pal;\n    int flipX;\n    int flipY;\n    int prio;\n    int size;\n};\n\nenum ErrorCode itp_evaluateSimpleAttributes(struct Core *core, struct SimpleAttributes *attrs);\n\nstruct TypedValue itp_evaluateCharAttributes(struct Core *core, union CharacterAttributes oldAttr);\nstruct TypedValue itp_evaluateDisplayAttributes(struct Core *core, union DisplayAttributes oldAttr);\nstruct TypedValue itp_evaluateLFOAttributes(struct Core *core, union LFOAttributes oldAttr);\n\n#endif /* interpreter_utils_h */\n"
  },
  {
    "path": "core/interpreter/labels.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"labels.h\"\n#include \"interpreter.h\"\n\nenum ErrorCode lab_pushLabelStackItem(struct Interpreter *interpreter, enum LabelType type, struct Token *token)\n{\n    if (interpreter->numLabelStackItems >= MAX_LABEL_STACK_ITEMS) return ErrorStackOverflow;\n    struct LabelStackItem *item = &interpreter->labelStackItems[interpreter->numLabelStackItems];\n    item->type = type;\n    item->token = token;\n    interpreter->numLabelStackItems++;\n    return ErrorNone;\n}\n\nstruct LabelStackItem *lab_popLabelStackItem(struct Interpreter *interpreter)\n{\n    if (interpreter->numLabelStackItems > 0)\n    {\n        interpreter->numLabelStackItems--;\n        return &interpreter->labelStackItems[interpreter->numLabelStackItems];\n    }\n    return NULL;\n}\n\nstruct LabelStackItem *lab_peekLabelStackItem(struct Interpreter *interpreter)\n{\n    if (interpreter->numLabelStackItems > 0)\n    {\n        return &interpreter->labelStackItems[interpreter->numLabelStackItems - 1];\n    }\n    return NULL;\n}\n\nstruct LabelStackItem *lab_searchLabelStackItem(struct Interpreter *interpreter, enum LabelType types[], int numTypes)\n{\n    int i = interpreter->numLabelStackItems - 1;\n    while (i >= 0)\n    {\n        struct LabelStackItem *item = &interpreter->labelStackItems[i];\n        for (int j = 0; j < numTypes; j++)\n        {\n            if (item->type == types[j])\n            {\n                return item;\n            }\n        }\n        --i;\n    }\n    return NULL;\n}\n"
  },
  {
    "path": "core/interpreter/labels.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef labels_h\n#define labels_h\n\n#include <stdio.h>\n\nstruct Interpreter;\nstruct Token;\n\nenum LabelType {\n    LabelTypeIF,\n    LabelTypeELSE,\n    LabelTypeELSEIF,\n    LabelTypeFOR,\n    LabelTypeFORVar,\n    LabelTypeFORLimit,\n    LabelTypeGOSUB,\n    LabelTypeDO,\n    LabelTypeREPEAT,\n    LabelTypeWHILE,\n    LabelTypeSUB,\n    LabelTypeCALL,\n    LabelTypeONCALL,\n};\n\nstruct LabelStackItem {\n    enum LabelType type;\n    struct Token *token;\n};\n\nenum ErrorCode lab_pushLabelStackItem(struct Interpreter *interpreter, enum LabelType type, struct Token *token);\nstruct LabelStackItem *lab_popLabelStackItem(struct Interpreter *interpreter);\nstruct LabelStackItem *lab_peekLabelStackItem(struct Interpreter *interpreter);\nstruct LabelStackItem *lab_searchLabelStackItem(struct Interpreter *interpreter, enum LabelType types[], int numTypes);\n\n#endif /* labels_h */\n"
  },
  {
    "path": "core/interpreter/rcstring.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"rcstring.h\"\n#include <stdlib.h>\n#include <string.h>\n\nstruct RCString *rcstring_new(const char *chars, size_t len)\n{\n    size_t size = sizeof(struct RCString) + len;\n    struct RCString *string = malloc(size);\n    if (string)\n    {\n        string->refCount = 1; // retain\n        if (chars)\n        {\n            memcpy(string->chars, chars, len);\n        }\n        string->chars[len] = 0; // end of string\n    }\n    return string;\n}\n\nvoid rcstring_retain(struct RCString *string)\n{\n    string->refCount++;\n}\n\nvoid rcstring_release(struct RCString *string)\n{\n    string->refCount--;\n    if (string->refCount == 0)\n    {\n        free((void *)string);\n    }\n}\n"
  },
  {
    "path": "core/interpreter/rcstring.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef string_h\n#define string_h\n\n#include <stdio.h>\n\nstruct RCString {\n    int refCount;\n    char chars[1]; // ...\n};\n\nstruct RCString *rcstring_new(const char *chars, size_t len);\nvoid rcstring_retain(struct RCString *string);\nvoid rcstring_release(struct RCString *string);\n\n#endif /* string_h */\n"
  },
  {
    "path": "core/interpreter/string_utils.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"string_utils.h\"\n#include <stdlib.h>\n#include <string.h>\n\nconst char *uppercaseString(const char *source)\n{\n    size_t len = strlen(source);\n    char *buffer = malloc(len + 1);\n    if (buffer)\n    {\n        const char *sourceChar = source;\n        char *destChar = buffer;\n        char finalChar = 0;\n        while (*sourceChar)\n        {\n            finalChar = *sourceChar++;\n            if (finalChar >= 'a' && finalChar <= 'z')\n            {\n                finalChar -= 32;\n            }\n            *destChar++ = finalChar;\n        }\n        *destChar = 0;\n    }\n    return buffer;\n}\n\nconst char *lineString(const char *source, int pos)\n{\n    const char *start = &source[pos];\n    const char *end = &source[pos];\n    while (start - 1 >= source && *(start - 1) != '\\n')\n    {\n        start--;\n    }\n    while (*(end + 1) != 0 && *end != '\\n' && *end != 0)\n    {\n        end++;\n    }\n    if (end > start)\n    {\n        size_t len = end - start;\n        char *buffer = malloc(len + 1);\n        if (buffer)\n        {\n            strncpy(buffer, start, len);\n            buffer[len] = 0;\n            return buffer;\n        }\n    }\n    return NULL;\n}\n\nint lineNumber(const char *source, int pos)\n{\n    int line = 1;\n    for (int i = 0; i < pos; i++)\n    {\n        if (source[i] == '\\n')\n        {\n            line++;\n        }\n    }\n    return line;\n}\n\nvoid stringConvertCopy(char *dest, const char *source, size_t length)\n{\n    char *currDstChar = dest;\n    for (int i = 0; i < length; i++)\n    {\n        char currSrcChar = source[i];\n        if (currSrcChar != '\\r')\n        {\n            *currDstChar = currSrcChar;\n            currDstChar++;\n        }\n    }\n    *currDstChar = 0;\n}\n"
  },
  {
    "path": "core/interpreter/string_utils.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef string_utils_h\n#define string_utils_h\n\n#include <stdio.h>\n\nconst char *uppercaseString(const char *source);\nconst char *lineString(const char *source, int pos);\nint lineNumber(const char *source, int pos);\nvoid stringConvertCopy(char *dest, const char *source, size_t length);\n\n#endif /* string_utils_h */\n"
  },
  {
    "path": "core/interpreter/token.c",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"token.h\"\n\nconst char *TokenStrings[] = {\n    NULL,\n    \n    NULL,\n    NULL,\n    NULL,\n    NULL,\n    NULL,\n    \n    // Signs\n    \":\",\n    \",\",\n    \";\",\n    \"'\",\n    NULL,\n    \n    // Operators\n    \"=\",\n    \">=\",\n    \"<=\",\n    \"<>\",\n    \">\",\n    \"<\",\n    \"(\",\n    \")\",\n    \"+\",\n    \"-\",\n    \"*\",\n    \"/\",\n    \"\\\\\",\n    \"^\",\n    \"AND\",\n    \"NOT\",\n    \"OR\",\n    \"XOR\",\n    \"MOD\",\n    \n    // Commands/Functions\n    \"ABS\",\n    \"ACOS\",\n    \"ADD\",\n    \"ASC\",\n    \"ASIN\",\n    \"ATAN\",\n    \"ATTR\",\n    \"BG\",\n    \"BIN$\",\n    \"BUTTON\",\n    \"CALL\",\n    \"CELL.A\",\n    \"CELL.C\",\n    \"CELL\",\n    \"CHAR\",\n    \"CHR$\",\n    \"CLS\",\n    \"CLW\",\n    \"COLOR\",\n    \"COPY\",\n    \"COS\",\n    \"CURSOR.X\",\n    \"CURSOR.Y\",\n    \"DATA\",\n    \"DEC\",\n    \"DIM\",\n    \"DISPLAY\",\n    \"DOWN\",\n    \"DO\",\n    \"ELSE\",\n    \"END\",\n    \"ENVELOPE\",\n    \"EXIT\",\n    \"EXP\",\n    \"FILE$\",\n    \"FILES\",\n    \"FILL\",\n    \"FLIP\",\n    \"FONT\",\n    \"FOR\",\n    \"FSIZE\",\n    \"GAMEPAD\",\n    \"GLOBAL\",\n    \"GOSUB\",\n    \"GOTO\",\n    \"HEX$\",\n    \"HCOS\",\n    \"HIT\",\n    \"HSIN\",\n    \"HTAN\",\n    \"IF\",\n    \"INC\",\n    \"INKEY$\",\n    \"INPUT\",\n    \"INSTR\",\n    \"INT\",\n    \"KEYBOARD\",\n    \"LEFT$\",\n    \"LEFT\",\n    \"LEN\",\n    \"LET\",\n    \"LFO.A\",\n    \"LFO\",\n    \"LOAD\",\n    \"LOCATE\",\n    \"LOG\",\n    \"LOOP\",\n    \"MAX\",\n    \"MCELL.A\",\n    \"MCELL.C\",\n    \"MCELL\",\n    \"MID$\",\n    \"MIN\",\n    \"MUSIC\",\n    \"NEXT\",\n    \"NUMBER\",\n    \"OFF\",\n    \"ON\",\n    \"OPTIONAL\",\n    \"PALETTE\",\n    \"PAL\",\n    \"PAUSE\",\n    \"PEEKL\",\n    \"PEEKW\",\n    \"PEEK\",\n    \"PI\",\n    \"PLAY\",\n    \"POKEL\",\n    \"POKEW\",\n    \"POKE\",\n    \"PRINT\",\n    \"PRIO\",\n    \"RANDOMIZE\",\n    \"RASTER\",\n    \"READ\",\n    \"REM\",\n    \"REPEAT\",\n    \"RESTORE\",\n    \"RETURN\",\n    \"RIGHT$\",\n    \"RIGHT\",\n    \"RND\",\n    \"ROL\",\n    \"ROM\",\n    \"ROR\",\n    \"SAVE\",\n    \"SCROLL.X\",\n    \"SCROLL.Y\",\n    \"SCROLL\",\n    \"SGN\",\n    \"SIN\",\n    \"SIZE\",\n    \"SOUND\",\n    \"SOURCE\",\n    \"SPRITE.A\",\n    \"SPRITE.C\",\n    \"SPRITE.X\",\n    \"SPRITE.Y\",\n    \"SPRITE\",\n    \"SQR\",\n    \"STEP\",\n    \"STOP\",\n    \"STR$\",\n    \"SUB\",\n    \"SWAP\",\n    \"SYSTEM\",\n    \"TAN\",\n    \"TAP\",\n    \"TEXT\",\n    \"THEN\",\n    \"TIMER\",\n    \"TINT\",\n    \"TOUCHSCREEN\",\n    \"TOUCH.X\",\n    \"TOUCH.Y\",\n    \"TOUCH\",\n    \"TO\",\n    \"TRACE\",\n    \"TRACK\",\n    \"UBOUND\",\n    \"UNTIL\",\n    \"UP\",\n    \"VAL\",\n    \"VBL\",\n    \"VIEW\",\n    \"VOLUME\",\n    \"WAIT\",\n    \"WAVE\",\n    \"WEND\",\n    \"WHILE\",\n    \"WINDOW\",\n    \n    // Reserved Keywords\n    NULL,\n    \"ANIM\",\n    \"CLOSE\",\n    \"DECLARE\",\n    \"DEF\",\n    \"FLASH\",\n    \"FN\",\n    \"FUNCTION\",\n    \"LBOUND\",\n    \"OPEN\",\n    \"OUTPUT\",\n    \"SHARED\",\n    \"STATIC\",\n    \"TEMPO\",\n    \"VOICE\",\n    \"WRITE\",\n\n    NULL\n};\n"
  },
  {
    "path": "core/interpreter/token.h",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef token_h\n#define token_h\n\n#include <stdio.h>\n#include \"rcstring.h\"\n\nenum TokenType {\n    TokenUndefined,\n    \n    TokenIdentifier,\n    TokenStringIdentifier,\n    TokenLabel,\n    TokenFloat,\n    TokenString,\n    \n    // Signs\n    TokenColon,\n    TokenComma,\n    TokenSemicolon,\n    TokenApostrophe,\n    TokenEol,\n    \n    // Operators\n    TokenEq,\n    TokenGrEq,\n    TokenLeEq,\n    TokenUneq,\n    TokenGr,\n    TokenLe,\n    TokenBracketOpen,\n    TokenBracketClose,\n    TokenPlus,\n    TokenMinus,\n    TokenMul,\n    TokenDiv,\n    TokenDivInt,\n    TokenPow,\n    TokenAND,\n    TokenNOT,\n    TokenOR,\n    TokenXOR,\n    TokenMOD,\n    \n    // Commands/Functions\n    TokenABS,\n    TokenACOS,\n    TokenADD,\n    TokenASC,\n    TokenASIN,\n    TokenATAN,\n    TokenATTR,\n    TokenBG,\n    TokenBIN,\n    TokenBUTTON,\n    TokenCALL,\n    TokenCELLA,\n    TokenCELLC,\n    TokenCELL,\n    TokenCHAR,\n    TokenCHR,\n    TokenCLS,\n    TokenCLW,\n    TokenCOLOR,\n    TokenCOPY,\n    TokenCOS,\n    TokenCURSORX,\n    TokenCURSORY,\n    TokenDATA,\n    TokenDEC,\n    TokenDIM,\n    TokenDISPLAY,\n    TokenDOWN,\n    TokenDO,\n    TokenELSE,\n    TokenEND,\n    TokenENVELOPE,\n    TokenEXIT,\n    TokenEXP,\n    TokenFILE,\n    TokenFILES,\n    TokenFILL,\n    TokenFLIP,\n    TokenFONT,\n    TokenFOR,\n    TokenFSIZE,\n    TokenGAMEPAD,\n    TokenGLOBAL,\n    TokenGOSUB,\n    TokenGOTO,\n    TokenHEX,\n    TokenHCOS,\n    TokenHIT,\n    TokenHSIN,\n    TokenHTAN,\n    TokenIF,\n    TokenINC,\n    TokenINKEY,\n    TokenINPUT,\n    TokenINSTR,\n    TokenINT,\n    TokenKEYBOARD,\n    TokenLEFTStr,\n    TokenLEFT,\n    TokenLEN,\n    TokenLET,\n    TokenLFOA,\n    TokenLFO,\n    TokenLOAD,\n    TokenLOCATE,\n    TokenLOG,\n    TokenLOOP,\n    TokenMAX,\n    TokenMCELLA,\n    TokenMCELLC,\n    TokenMCELL,\n    TokenMID,\n    TokenMIN,\n    TokenMUSIC,\n    TokenNEXT,\n    TokenNUMBER,\n    TokenOFF,\n    TokenON,\n    TokenOPTIONAL,\n    TokenPALETTE,\n    TokenPAL,\n    TokenPAUSE,\n    TokenPEEKL,\n    TokenPEEKW,\n    TokenPEEK,\n    TokenPI,\n    TokenPLAY,\n    TokenPOKEL,\n    TokenPOKEW,\n    TokenPOKE,\n    TokenPRINT,\n    TokenPRIO,\n    TokenRANDOMIZE,\n    TokenRASTER,\n    TokenREAD,\n    TokenREM,\n    TokenREPEAT,\n    TokenRESTORE,\n    TokenRETURN,\n    TokenRIGHTStr,\n    TokenRIGHT,\n    TokenRND,\n    TokenROL,\n    TokenROM,\n    TokenROR,\n    TokenSAVE,\n    TokenSCROLLX,\n    TokenSCROLLY,\n    TokenSCROLL,\n    TokenSGN,\n    TokenSIN,\n    TokenSIZE,\n    TokenSOUND,\n    TokenSOURCE,\n    TokenSPRITEA,\n    TokenSPRITEC,\n    TokenSPRITEX,\n    TokenSPRITEY,\n    TokenSPRITE,\n    TokenSQR,\n    TokenSTEP,\n    TokenSTOP,\n    TokenSTR,\n    TokenSUB,\n    TokenSWAP,\n    TokenSYSTEM,\n    TokenTAN,\n    TokenTAP,\n    TokenTEXT,\n    TokenTHEN,\n    TokenTIMER,\n    TokenTINT,\n    TokenTOUCHSCREEN,\n    TokenTOUCHX,\n    TokenTOUCHY,\n    TokenTOUCH,\n    TokenTO,\n    TokenTRACE,\n    TokenTRACK,\n    TokenUBOUND,\n    TokenUNTIL,\n    TokenUP,\n    TokenVAL,\n    TokenVBL,\n    TokenVIEW,\n    TokenVOLUME,\n    TokenWAIT,\n    TokenWAVE,\n    TokenWEND,\n    TokenWHILE,\n    TokenWINDOW,\n    \n    // Reserved Keywords\n    Token_reserved,\n    TokenANIM,\n    TokenCLOSE,\n    TokenDECLARE,\n    TokenDEF,\n    TokenFLASH,\n    TokenFN,\n    TokenFUNCTION,\n    TokenLBOUND,\n    TokenOPEN,\n    TokenOUTPUT,\n    TokenSHARED,\n    TokenSTATIC,\n    TokenTEMPO,\n    TokenVOICE,\n    TokenWRITE,\n    \n    Token_count\n};\n\nstruct Token {\n    enum TokenType type;\n    union {\n        float floatValue;\n        struct RCString *stringValue;\n        int symbolIndex;\n        struct Token *jumpToken;\n    };\n    int sourcePosition;\n};\n\nextern const char *TokenStrings[];\n\n#endif /* token_h */\n"
  },
  {
    "path": "core/interpreter/tokenizer.c",
    "content": "//\n// Copyright 2016-2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"tokenizer.h\"\n#include \"error.h\"\n#include \"charsets.h\"\n#include <string.h>\n#include <stdlib.h>\n#include \"string_utils.h\"\n\nstruct CoreError tok_tokenizeProgram(struct Tokenizer *tokenizer, const char *sourceCode)\n{\n    const char *uppercaseSourceCode = uppercaseString(sourceCode);\n    if (!uppercaseSourceCode) return err_makeCoreError(ErrorOutOfMemory, -1);\n    \n    struct CoreError error = tok_tokenizeUppercaseProgram(tokenizer, uppercaseSourceCode);\n    free((void *)uppercaseSourceCode);\n    \n    return error;\n}\n\nstruct CoreError tok_tokenizeUppercaseProgram(struct Tokenizer *tokenizer, const char *sourceCode)\n{\n    const char *character = sourceCode;\n    \n    // PROGRAM\n    \n    while (*character && *character != '#')\n    {\n        int tokenSourcePosition = (int)(character - sourceCode);\n        if (tokenizer->numTokens >= MAX_TOKENS - 1)\n        {\n            return err_makeCoreError(ErrorTooManyTokens, tokenSourcePosition);\n        }\n        struct Token *token = &tokenizer->tokens[tokenizer->numTokens];\n        token->sourcePosition = tokenSourcePosition;\n        \n        // line break \\n or \\n\\r\n        if (*character == '\\n')\n        {\n            token->type = TokenEol;\n            tokenizer->numTokens++;\n            character++;\n            if (*character == '\\r') { character++; }\n            continue;\n        }\n        \n        // line break \\r or \\r\\n\n        if (*character == '\\r')\n        {\n            token->type = TokenEol;\n            tokenizer->numTokens++;\n            character++;\n            if (*character == '\\n') { character++; }\n            continue;\n        }\n        \n        // space\n        if (*character == ' ' || *character == '\\t')\n        {\n            character++;\n            continue;\n        }\n        \n        // string\n        if (*character == '\"')\n        {\n            character++;\n            const char *firstCharacter = character;\n            while (*character && *character != '\"')\n            {\n                if (*character == '\\n')\n                {\n                    return err_makeCoreError(ErrorUnterminatedString, (int)(character - sourceCode));\n                }\n                else if (*character < 0)\n                {\n                    return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - sourceCode));\n                }\n                character++;\n            }\n            int len = (int)(character - firstCharacter);\n            struct RCString *string = rcstring_new(firstCharacter, len);\n            if (!string) return err_makeCoreError(ErrorOutOfMemory, tokenSourcePosition);\n            token->type = TokenString;\n            token->stringValue = string;\n            tokenizer->numTokens++;\n            character++;\n            continue;\n        }\n        \n        // number\n        if (strchr(CharSetDigits, *character))\n        {\n            float number = 0;\n            int afterDot = 0;\n            while (*character)\n            {\n                if (strchr(CharSetDigits, *character))\n                {\n                    int digit = (int)*character - (int)'0';\n                    if (afterDot == 0)\n                    {\n                        number *= 10;\n                        number += digit;\n                    }\n                    else\n                    {\n                        number += (float)digit / afterDot;\n                        afterDot *= 10;\n                    }\n                    character++;\n                }\n                else if (*character == '.' && afterDot == 0)\n                {\n                    afterDot = 10;\n                    character++;\n                }\n                else\n                {\n                    break;\n                }\n            }\n            token->type = TokenFloat;\n            token->floatValue = number;\n            tokenizer->numTokens++;\n            continue;\n        }\n        \n        // hex number\n        if (*character == '$')\n        {\n            character++;\n            int number = 0;\n            while (*character)\n            {\n                char *spos = strchr(CharSetHex, *character);\n                if (spos)\n                {\n                    int digit = (int)(spos - CharSetHex);\n                    number <<= 4;\n                    number += digit;\n                    character++;\n                }\n                else\n                {\n                    break;\n                }\n            }\n            token->type = TokenFloat;\n            token->floatValue = number;\n            tokenizer->numTokens++;\n            continue;\n        }\n        \n        // bin number\n        if (*character == '%')\n        {\n            character++;\n            int number = 0;\n            while (*character)\n            {\n                if (*character == '0' || *character == '1')\n                {\n                    int digit = *character - '0';\n                    number <<= 1;\n                    number += digit;\n                    character++;\n                }\n                else\n                {\n                    break;\n                }\n            }\n            token->type = TokenFloat;\n            token->floatValue = number;\n            tokenizer->numTokens++;\n            continue;\n        }\n        \n        // Keyword\n        enum TokenType foundKeywordToken = TokenUndefined;\n        for (int i = 0; i < Token_count; i++)\n        {\n            const char *keyword = TokenStrings[i];\n            if (keyword)\n            {\n                size_t keywordLen = strlen(keyword);\n                int keywordIsAlphaNum = strchr(CharSetAlphaNum, keyword[0]) != NULL;\n                for (int pos = 0; pos <= keywordLen; pos++)\n                {\n                    char textCharacter = character[pos];\n                    \n                    if (pos < keywordLen)\n                    {\n                        char symbCharacter = keyword[pos];\n                        if (symbCharacter != textCharacter)\n                        {\n                            // not matching\n                            break;\n                        }\n                    }\n                    else if (keywordIsAlphaNum && textCharacter && strchr(CharSetAlphaNum, textCharacter))\n                    {\n                        // matching, but word is longer, so seems to be an identifier\n                        break;\n                    }\n                    else\n                    {\n                        // symbol found!\n                        foundKeywordToken = i;\n                        character += keywordLen;\n                        break;\n                    }\n                }\n                if (foundKeywordToken != TokenUndefined)\n                {\n                    break;\n                }\n            }\n        }\n        if (foundKeywordToken != TokenUndefined)\n        {\n            if (foundKeywordToken == TokenREM || foundKeywordToken == TokenApostrophe)\n            {\n                // REM comment, skip until end of line\n                while (*character)\n                {\n                    if (*character < 0)\n                    {\n                        return err_makeCoreError(ErrorUnexpectedCharacter, (int)(character - sourceCode));\n                    }\n                    if (*character == '\\n')\n                    {\n                        character++;\n                        break;\n                    }\n                    character++;\n                }\n            }\n            else if (foundKeywordToken > Token_reserved)\n            {\n                return err_makeCoreError(ErrorReservedKeyword, tokenSourcePosition);\n            }\n            token->type = foundKeywordToken;\n            tokenizer->numTokens++;\n            continue;\n        }\n        \n        // Symbol\n        if (strchr(CharSetLetters, *character))\n        {\n            const char *firstCharacter = character;\n            char isString = 0;\n            while (*character)\n            {\n                if (strchr(CharSetAlphaNum, *character))\n                {\n                    character++;\n                }\n                else\n                {\n                    if (*character == '$')\n                    {\n                        isString = 1;\n                        character++;\n                    }\n                    break;\n                }\n            }\n            if (tokenizer->numSymbols >= MAX_SYMBOLS)\n            {\n                return err_makeCoreError(ErrorTooManySymbols, tokenSourcePosition);\n            }\n            int len = (int)(character - firstCharacter);\n            if (len >= SYMBOL_NAME_SIZE)\n            {\n                return err_makeCoreError(ErrorSymbolNameTooLong, tokenSourcePosition);\n            }\n            char symbolName[SYMBOL_NAME_SIZE];\n            memcpy(symbolName, firstCharacter, len);\n            symbolName[len] = 0;\n            int symbolIndex = -1;\n            // find existing symbol\n            for (int i = 0; i < MAX_SYMBOLS && tokenizer->symbols[i].name[0] != 0; i++)\n            {\n                if (strcmp(symbolName, tokenizer->symbols[i].name) == 0)\n                {\n                    symbolIndex = i;\n                    break;\n                }\n            }\n            if (symbolIndex == -1)\n            {\n                // add new symbol\n                strcpy(tokenizer->symbols[tokenizer->numSymbols].name, symbolName);\n                symbolIndex = tokenizer->numSymbols++;\n            }\n            if (isString)\n            {\n                token->type = TokenStringIdentifier;\n            }\n            else if (*character == ':')\n            {\n                token->type = TokenLabel;\n                character++;\n                enum ErrorCode errorCode = tok_setJumpLabel(tokenizer, symbolIndex, token + 1);\n                if (errorCode != ErrorNone) return err_makeCoreError(errorCode, tokenSourcePosition);\n            }\n            else\n            {\n                token->type = TokenIdentifier;\n                if (tokenizer->numTokens > 0 && tokenizer->tokens[tokenizer->numTokens - 1].type == TokenSUB)\n                {\n                    enum ErrorCode errorCode = tok_setSub(tokenizer, symbolIndex, token + 1);\n                    if (errorCode != ErrorNone) return err_makeCoreError(errorCode, tokenSourcePosition);\n                }\n            }\n            token->symbolIndex = symbolIndex;\n            tokenizer->numTokens++;\n            continue;\n        }\n        \n        // Unexpected character\n        return err_makeCoreError(ErrorUnexpectedCharacter, tokenSourcePosition);\n    }\n    \n    // add EOL to the end\n    struct Token *token = &tokenizer->tokens[tokenizer->numTokens];\n    token->sourcePosition = (int)(character - sourceCode);\n    token->type = TokenEol;\n    tokenizer->numTokens++;\n    \n    return err_noCoreError();\n}\n\nvoid tok_freeTokens(struct Tokenizer *tokenizer)\n{\n    // Free string tokens\n    for (int i = 0; i < tokenizer->numTokens; i++)\n    {\n        struct Token *token = &tokenizer->tokens[i];\n        if (token->type == TokenString)\n        {\n            rcstring_release(token->stringValue);\n        }\n    }\n    memset(tokenizer, 0, sizeof(struct Tokenizer));\n}\n\nstruct JumpLabelItem *tok_getJumpLabel(struct Tokenizer *tokenizer, int symbolIndex)\n{\n    struct JumpLabelItem *item;\n    for (int i = 0; i < tokenizer->numJumpLabelItems; i++)\n    {\n        item = &tokenizer->jumpLabelItems[i];\n        if (item->symbolIndex == symbolIndex)\n        {\n            return item;\n        }\n    }\n    return NULL;\n}\n\nenum ErrorCode tok_setJumpLabel(struct Tokenizer *tokenizer, int symbolIndex, struct Token *token)\n{\n    if (tok_getJumpLabel(tokenizer, symbolIndex) != NULL)\n    {\n        return ErrorLabelAlreadyDefined;\n    }\n    if (tokenizer->numJumpLabelItems >= MAX_JUMP_LABEL_ITEMS)\n    {\n        return ErrorTooManyLabels;\n    }\n    struct JumpLabelItem *item = &tokenizer->jumpLabelItems[tokenizer->numJumpLabelItems];\n    item->symbolIndex = symbolIndex;\n    item->token = token;\n    tokenizer->numJumpLabelItems++;\n    return ErrorNone;\n}\n\nstruct SubItem *tok_getSub(struct Tokenizer *tokenizer, int symbolIndex)\n{\n    struct SubItem *item;\n    for (int i = 0; i < tokenizer->numSubItems; i++)\n    {\n        item = &tokenizer->subItems[i];\n        if (item->symbolIndex == symbolIndex)\n        {\n            return item;\n        }\n    }\n    return NULL;\n}\n\nenum ErrorCode tok_setSub(struct Tokenizer *tokenizer, int symbolIndex, struct Token *token)\n{\n    if (tok_getSub(tokenizer, symbolIndex) != NULL)\n    {\n        return ErrorSubAlreadyDefined;\n    }\n    if (tokenizer->numSubItems >= MAX_SUB_ITEMS)\n    {\n        return ErrorTooManySubprograms;\n    }\n    struct SubItem *item = &tokenizer->subItems[tokenizer->numSubItems];\n    item->symbolIndex = symbolIndex;\n    item->token = token;\n    tokenizer->numSubItems++;\n    return ErrorNone;\n}\n"
  },
  {
    "path": "core/interpreter/tokenizer.h",
    "content": "//\n// Copyright 2016-2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef tokenizer_h\n#define tokenizer_h\n\n#include <stdio.h>\n#include \"interpreter_config.h\"\n#include \"token.h\"\n\nstruct Symbol {\n    char name[SYMBOL_NAME_SIZE];\n};\n\nstruct JumpLabelItem {\n    int symbolIndex;\n    struct Token *token;\n};\n\nstruct SubItem {\n    int symbolIndex;\n    struct Token *token;\n};\n\nstruct Tokenizer\n{\n    struct Token tokens[MAX_TOKENS];\n    int numTokens;\n    struct Symbol symbols[MAX_SYMBOLS];\n    int numSymbols;\n    \n    struct JumpLabelItem jumpLabelItems[MAX_JUMP_LABEL_ITEMS];\n    int numJumpLabelItems;\n    struct SubItem subItems[MAX_SUB_ITEMS];\n    int numSubItems;\n};\n\nstruct CoreError tok_tokenizeProgram(struct Tokenizer *tokenizer, const char *sourceCode);\nstruct CoreError tok_tokenizeUppercaseProgram(struct Tokenizer *tokenizer, const char *sourceCode);\nvoid tok_freeTokens(struct Tokenizer *tokenizer);\nstruct JumpLabelItem *tok_getJumpLabel(struct Tokenizer *tokenizer, int symbolIndex);\nenum ErrorCode tok_setJumpLabel(struct Tokenizer *tokenizer, int symbolIndex, struct Token *token);\nstruct SubItem *tok_getSub(struct Tokenizer *tokenizer, int symbolIndex);\nenum ErrorCode tok_setSub(struct Tokenizer *tokenizer, int symbolIndex, struct Token *token);\n\n#endif /* tokenizer_h */\n"
  },
  {
    "path": "core/interpreter/value.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"value.h\"\n\nunion Value ValueDummy = {0};\n\nstruct TypedValue val_makeError(enum ErrorCode errorCode)\n{\n    struct TypedValue value;\n    value.type = ValueTypeError;\n    value.v.errorCode = errorCode;\n    return value;\n}\n"
  },
  {
    "path": "core/interpreter/value.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef value_h\n#define value_h\n\n#include <stdio.h>\n#include \"error.h\"\n#include \"rcstring.h\"\n\nenum ValueType {\n    ValueTypeNull,\n    ValueTypeError,\n    ValueTypeFloat,\n    ValueTypeString\n};\n\nunion Value {\n    float floatValue;\n    struct RCString *stringValue;\n    union Value *reference;\n    enum ErrorCode errorCode;\n};\n\nstruct TypedValue {\n    enum ValueType type;\n    union Value v;\n};\n\nenum TypeClass {\n    TypeClassAny,\n    TypeClassNumeric,\n    TypeClassString\n};\n\nextern union Value ValueDummy;\n\nstruct TypedValue val_makeError(enum ErrorCode errorCode);\n\n#endif /* value_h */\n"
  },
  {
    "path": "core/interpreter/variables.c",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"variables.h\"\n#include \"core.h\"\n#include <stdlib.h>\n#include <string.h>\n\nstruct SimpleVariable *var_getSimpleVariable(struct Interpreter *interpreter, int symbolIndex, int subLevel)\n{\n    struct SimpleVariable *variable = NULL;\n    for (int i = interpreter->numSimpleVariables - 1; i >= 0; i--)\n    {\n        variable = &interpreter->simpleVariables[i];\n        if (variable->symbolIndex == symbolIndex && (variable->subLevel == subLevel || variable->subLevel == SUB_LEVEL_GLOBAL))\n        {\n            // variable found\n            return variable;\n        }\n    }\n    return NULL;\n}\n\nstruct SimpleVariable *var_createSimpleVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int subLevel, enum ValueType type, union Value *valueReference)\n{\n    if (interpreter->numSimpleVariables >= MAX_SIMPLE_VARIABLES)\n    {\n        *errorCode = ErrorOutOfMemory;\n        return NULL;\n    }\n    if (subLevel > 127)\n    {\n        *errorCode = ErrorStackOverflow;\n        return NULL;\n    }\n    struct SimpleVariable *variable = &interpreter->simpleVariables[interpreter->numSimpleVariables];\n    interpreter->numSimpleVariables++;\n    memset(variable, 0, sizeof(struct SimpleVariable));\n    variable->symbolIndex = symbolIndex;\n    variable->subLevel = subLevel;\n    variable->type = type;\n    if (valueReference)\n    {\n        variable->isReference = 1;\n        variable->v.reference = valueReference;\n    }\n    else\n    {\n        variable->isReference = 0;\n        if (type == ValueTypeString)\n        {\n            // assign global NullString\n            variable->v.stringValue = interpreter->nullString;\n            rcstring_retain(variable->v.stringValue);\n        }\n    }\n    return variable;\n}\n\nvoid var_freeSimpleVariables(struct Interpreter *interpreter, int minSubLevel)\n{\n    for (int i = interpreter->numSimpleVariables - 1; i >= 0; i--)\n    {\n        struct SimpleVariable *variable = &interpreter->simpleVariables[i];\n        if (variable->subLevel < minSubLevel)\n        {\n            break;\n        }\n        else\n        {\n            if (!variable->isReference && variable->type == ValueTypeString)\n            {\n                rcstring_release(variable->v.stringValue);\n            }\n            interpreter->numSimpleVariables--;\n        }\n    }\n}\n\nstruct ArrayVariable *var_getArrayVariable(struct Interpreter *interpreter, int symbolIndex, int subLevel)\n{\n    struct ArrayVariable *variable = NULL;\n    for (int i = interpreter->numArrayVariables - 1; i >= 0; i--)\n    {\n        variable = &interpreter->arrayVariables[i];\n        if (variable->symbolIndex == symbolIndex && (variable->subLevel == subLevel || variable->subLevel == SUB_LEVEL_GLOBAL))\n        {\n            // variable found\n            return variable;\n        }\n    }\n    return NULL;\n}\n\nunion Value *var_getArrayValue(struct Interpreter *interpreter, struct ArrayVariable *variable, int *indices)\n{\n    int offset = 0;\n    int factor = 1;\n    for (int i = variable->numDimensions - 1; i >= 0; i--)\n    {\n        offset += indices[i] * factor;\n        factor *= variable->dimensionSizes[i];\n    }\n    union Value *value = &variable->values[offset];\n    if (variable->type == ValueTypeString && !value->stringValue)\n    {\n        // string variable was still uninitialized, assign global NullString\n        value->stringValue = interpreter->nullString;\n        rcstring_retain(value->stringValue);\n    }\n    return value;\n}\n\nstruct ArrayVariable *var_dimVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int numDimensions, int *dimensionSizes)\n{\n    if (var_getArrayVariable(interpreter, symbolIndex, interpreter->subLevel))\n    {\n        *errorCode = ErrorArrayAlreadyDimensionized;\n        return NULL;\n    }\n    if (var_getSimpleVariable(interpreter, symbolIndex, interpreter->subLevel))\n    {\n        *errorCode = ErrorVariableAlreadyUsed;\n        return NULL;\n    }\n    if (interpreter->numArrayVariables >= MAX_ARRAY_VARIABLES)\n    {\n        *errorCode = ErrorOutOfMemory;\n        return NULL;\n    }\n    struct ArrayVariable *variable = &interpreter->arrayVariables[interpreter->numArrayVariables];\n    interpreter->numArrayVariables++;\n    memset(variable, 0, sizeof(struct ArrayVariable));\n    variable->symbolIndex = symbolIndex;\n    variable->subLevel = interpreter->subLevel;\n    variable->isReference = 0;\n    variable->numDimensions = numDimensions;\n    size_t size = 1;\n    for (int i = 0; i < numDimensions; i++)\n    {\n        size *= dimensionSizes[i];\n        variable->dimensionSizes[i] = dimensionSizes[i];\n    }\n    if (size > MAX_ARRAY_SIZE)\n    {\n        *errorCode = ErrorOutOfMemory;\n        return NULL;\n    }\n    variable->values = calloc(size, sizeof(union Value));\n    if (!variable->values) exit(EXIT_FAILURE);\n    \n    variable->numValues = (int)size;\n    return variable;\n}\n\nstruct ArrayVariable *var_createArrayVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int subLevel, struct ArrayVariable *arrayReference)\n{\n    if (interpreter->numArrayVariables >= MAX_ARRAY_VARIABLES)\n    {\n        *errorCode = ErrorOutOfMemory;\n        return NULL;\n    }\n    if (subLevel > 127)\n    {\n        *errorCode = ErrorStackOverflow;\n        return NULL;\n    }\n    struct ArrayVariable *variable = &interpreter->arrayVariables[interpreter->numArrayVariables];\n    interpreter->numArrayVariables++;\n    memset(variable, 0, sizeof(struct ArrayVariable));\n    variable->symbolIndex = symbolIndex;\n    variable->subLevel = subLevel;\n    variable->isReference = 1;\n    variable->type = arrayReference->type;\n    int numDimensions = arrayReference->numDimensions;\n    variable->numDimensions = numDimensions;\n    for (int i = 0; i < numDimensions; i++)\n    {\n        variable->dimensionSizes[i] = arrayReference->dimensionSizes[i];\n    }\n    variable->values = arrayReference->values;\n    return variable;\n}\n\nvoid var_freeArrayVariables(struct Interpreter *interpreter, int minSubLevel)\n{\n    for (int i = interpreter->numArrayVariables - 1; i >= 0; i--)\n    {\n        struct ArrayVariable *variable = &interpreter->arrayVariables[i];\n        if (variable->subLevel < minSubLevel)\n        {\n            break;\n        }\n        else\n        {\n            if (!variable->isReference)\n            {\n                if (variable->type == ValueTypeString)\n                {\n                    int numElements = 1;\n                    for (int di = 0; di < variable->numDimensions; di++)\n                    {\n                        numElements *= variable->dimensionSizes[di];\n                    }\n                    for (int ei = 0; ei < numElements; ei++)\n                    {\n                        union Value *value = &variable->values[ei];\n                        if (value->stringValue)\n                        {\n                            rcstring_release(value->stringValue);\n                        }\n                    }\n                }\n                free(variable->values);\n                variable->values = NULL;\n            }\n            interpreter->numArrayVariables--;\n        }\n    }\n}\n"
  },
  {
    "path": "core/interpreter/variables.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef variables_h\n#define variables_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include \"interpreter_config.h\"\n#include \"value.h\"\n\n#define SUB_LEVEL_GLOBAL -1\n\nstruct Core;\nstruct Interpreter;\n\nstruct SimpleVariable {\n    int symbolIndex;\n    int8_t subLevel;\n    int8_t isReference:1;\n    enum ValueType type;\n    union Value v;\n};\n\nstruct ArrayVariable {\n    int symbolIndex;\n    int8_t subLevel;\n    int8_t isReference:1;\n    enum ValueType type;\n    int numDimensions;\n    int dimensionSizes[MAX_ARRAY_DIMENSIONS];\n    int numValues;\n    union Value *values;\n};\n\nstruct SimpleVariable *var_getSimpleVariable(struct Interpreter *interpreter, int symbolIndex, int subLevel);\nstruct SimpleVariable *var_createSimpleVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int subLevel, enum ValueType type, union Value *valueReference);\nvoid var_freeSimpleVariables(struct Interpreter *interpreter, int minSubLevel);\n\nstruct ArrayVariable *var_getArrayVariable(struct Interpreter *interpreter, int symbolIndex, int subLevel);\nunion Value *var_getArrayValue(struct Interpreter *interpreter, struct ArrayVariable *variable, int *indices);\nstruct ArrayVariable *var_dimVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int numDimensions, int *dimensionSizes);\nstruct ArrayVariable *var_createArrayVariable(struct Interpreter *interpreter, enum ErrorCode *errorCode, int symbolIndex, int subLevel, struct ArrayVariable *arrayReference);\nvoid var_freeArrayVariables(struct Interpreter *interpreter, int minSubLevel);\n\n#endif /* variables_h */\n"
  },
  {
    "path": "core/libraries/audio_lib.c",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"audio_lib.h\"\n#include \"core.h\"\n#include <math.h>\n\n#define SOUND_SIZE 8\n#define PATTERN_SIZE 4\n#define ROW_SIZE 3\n\n#define PATTERNS_OFFSET (NUM_SOUNDS * SOUND_SIZE)\n#define TRACKS_OFFSET (PATTERNS_OFFSET + NUM_PATTERNS * PATTERN_SIZE)\n\nstruct TrackRow {\n    int note;\n    int sound;\n    int volume;\n    int command;\n    int parameter;\n};\n\nvoid audlib_updateMusic(struct AudioLib *lib);\nvoid audlib_updateTrack(struct AudioLib *lib, int voiceIndex);\nvoid audlib_setPitch(struct Voice *voice, float pitch);\nbool audlib_isPatternEmpty(struct AudioLib *lib, int sourceAddress, int pattern);\nint audlib_getLoopStart(struct AudioLib *lib, int sourceAddress, int pattern);\nint audlib_getLoop(struct AudioLib *lib, int sourceAddress, int pattern, int param);\nint audlib_getTrack(struct AudioLib *lib, int sourceAddress, int pattern, int voice);\nstruct TrackRow audlib_getTrackRow(struct AudioLib *lib, int sourceAddress, int track, int row);\nvoid audlib_playRow(struct AudioLib *lib, struct ComposerPlayer *player, int track, int voice);\nvoid audlib_command(struct AudioLib *lib, struct Voice *voice, struct ComposerPlayer *player, int command, int parameter);\n\n\nvoid audlib_play(struct AudioLib *lib, int voiceIndex, float pitch, int len, int sound)\n{\n    struct Core *core = lib->core;\n    struct Voice *voice = &core->machine->audioRegisters.voices[voiceIndex];\n    \n    audlib_setPitch(voice, pitch);\n    \n    if (sound != -1)\n    {\n        audlib_copySound(lib, lib->sourceAddress, sound, voiceIndex);\n    }\n    \n    if (len != -1)\n    {\n        voice->length = len;\n        voice->attr.timeout = (len > 0) ? 1 : 0;\n    }\n    voice->status.init = 1;\n    voice->status.gate = 1;\n    \n    machine_enableAudio(core);\n}\n\nvoid audlib_copySound(struct AudioLib *lib, int sourceAddress, int sound, int voiceIndex)\n{\n    int addr = sourceAddress + sound * 8;\n    int dest = 0xFF40 + voiceIndex * sizeof(struct Voice) + 4;\n    for (int i = 0; i < 8; i++)\n    {\n        int peek = machine_peek(lib->core, addr++);\n        machine_poke(lib->core, dest++, peek);\n    }\n    lib->core->interpreter->cycles += 8;\n}\n\nvoid audlib_playMusic(struct AudioLib *lib, int startPattern)\n{\n    struct ComposerPlayer *player = &lib->musicPlayer;\n    player->sourceAddress = lib->sourceAddress;\n    player->index = startPattern;\n    player->tick = -1;\n    player->row = 0;\n    player->speed = 8;\n    player->willBreak = false;\n    \n    machine_enableAudio(lib->core);\n}\n\nvoid audlib_playTrack(struct AudioLib *lib, int track, int voiceIndex)\n{\n    struct ComposerPlayer *player = &lib->trackPlayers[voiceIndex];\n    player->sourceAddress = lib->sourceAddress;\n    player->index = track;\n    player->tick = -1;\n    player->row = 0;\n    player->speed = 8;\n    player->willBreak = false;\n    \n    machine_enableAudio(lib->core);\n}\n\nvoid audlib_stopAll(struct AudioLib *lib)\n{\n    lib->musicPlayer.speed = 0;\n    for (int i = 0; i < NUM_VOICES; i++)\n    {\n        struct Voice *voice = &lib->core->machine->audioRegisters.voices[i];\n        voice->status.gate = 0;\n        lib->trackPlayers[i].speed = 0;\n    }\n}\n\nvoid audlib_stopVoice(struct AudioLib *lib, int voiceIndex)\n{\n    struct Voice *voice = &lib->core->machine->audioRegisters.voices[voiceIndex];\n    voice->status.gate = 0;\n    lib->trackPlayers[voiceIndex].speed = 0;\n}\n\nvoid audlib_update(struct AudioLib *lib)\n{\n    if (lib->musicPlayer.speed)\n    {\n        audlib_updateMusic(lib);\n    }\n    for (int v = 0; v < NUM_VOICES; v++)\n    {\n        if (lib->trackPlayers[v].speed)\n        {\n            audlib_updateTrack(lib, v);\n        }\n    }\n\n}\n\n\nvoid audlib_updateMusic(struct AudioLib *lib)\n{\n    struct ComposerPlayer *player = &lib->musicPlayer;\n    if (player->tick == -1)\n    {\n        player->tick = 0;\n    }\n    else if (player->tick == 0)\n    {\n        if (player->willBreak)\n        {\n            player->row = 0;\n            player->willBreak = false;\n        }\n        else\n        {\n            player->row = (player->row + 1) % NUM_TRACK_ROWS;\n        }\n        if (player->row == 0 && player->speed)\n        {\n            if (audlib_getLoop(lib, player->sourceAddress, player->index, 2) == 1)\n            {\n                player->speed = 0;\n                return;\n            }\n            if (audlib_getLoop(lib, player->sourceAddress, player->index, 1) == 1)\n            {\n                player->index = audlib_getLoopStart(lib, player->sourceAddress, player->index);\n            }\n            else\n            {\n                int p = player->index + 1;\n                if (p < NUM_PATTERNS)\n                {\n                    if (audlib_isPatternEmpty(lib, player->sourceAddress, p))\n                    {\n                        player->speed = 0;\n                        return;\n                    }\n                    else\n                    {\n                        player->index = p;\n                    }\n                }\n                else\n                {\n                    player->speed = 0;\n                    return;\n                }\n            }\n        }\n    }\n    if (player->tick == 0)\n    {\n        for (int v = 0; v < NUM_VOICES; v++)\n        {\n            // play only if no other track is playing on that voice\n            if (lib->trackPlayers[v].speed == 0)\n            {\n                int track = audlib_getTrack(lib, player->sourceAddress, player->index, v);\n                if (track >= 0)\n                {\n                    audlib_playRow(lib, player, track, v);\n                }\n            }\n        }\n        if (player->speed == 0)\n        {\n            return;\n        }\n    }\n    player->tick = (player->tick + 1) % player->speed;\n}\n\nvoid audlib_updateTrack(struct AudioLib *lib, int voiceIndex)\n{\n    struct ComposerPlayer *player = &lib->trackPlayers[voiceIndex];\n    if (player->tick == -1)\n    {\n        player->tick = 0;\n    }\n    else if (player->tick == 0)\n    {\n        player->row = (player->row + 1) % NUM_TRACK_ROWS;\n        if (player->row == 0 || player->willBreak)\n        {\n            player->willBreak = false;\n            player->speed = 0;\n            return;\n        }\n    }\n    if (player->tick == 0)\n    {\n        audlib_playRow(lib, player, player->index, voiceIndex);\n        if (player->speed == 0)\n        {\n            return;\n        }\n    }\n    player->tick = (player->tick + 1) % player->speed;\n}\n\nvoid audlib_setPitch(struct Voice *voice, float pitch)\n{\n    int f = 16.0 * 440.0 * pow(2.0, (pitch - 58.0) / 12.0);\n    voice->frequencyLow = f & 0xFF;\n    voice->frequencyHigh = f >> 8;\n}\n\nbool audlib_isPatternEmpty(struct AudioLib *lib, int sourceAddress, int pattern)\n{\n    for (int v = 0; v < NUM_VOICES; v++)\n    {\n        if (audlib_getTrack(lib, sourceAddress, pattern, v) >= 0)\n        {\n            return false;\n        }\n    }\n    return true;\n}\n\nint audlib_getLoopStart(struct AudioLib *lib, int sourceAddress, int pattern)\n{\n    for (int p = pattern; p >= 0; p--)\n    {\n        if (audlib_getLoop(lib, sourceAddress, p, 0) == 1)\n        {\n            return p;\n        }\n    }\n    return 0;\n}\n\nint audlib_getLoop(struct AudioLib *lib, int sourceAddress, int pattern, int param)\n{\n    int a = sourceAddress + PATTERNS_OFFSET + pattern * PATTERN_SIZE + param;\n    return machine_peek(lib->core, a) >> 7;\n}\n\nint audlib_getTrack(struct AudioLib *lib, int sourceAddress, int pattern, int voice)\n{\n    int a = sourceAddress + PATTERNS_OFFSET + pattern * PATTERN_SIZE + voice;\n    int track = machine_peek(lib->core, a) & 0x7F;\n    if (track == 0x40)\n    {\n        track = -1;\n    }\n    return track;\n}\n\nstruct TrackRow audlib_getTrackRow(struct AudioLib *lib, int sourceAddress, int track, int row)\n{\n    struct TrackRow trackRow;\n    int a = sourceAddress + TRACKS_OFFSET + track * NUM_TRACK_ROWS * ROW_SIZE + row * ROW_SIZE;\n    struct Core *core = lib->core;\n    trackRow.note = machine_peek(core, a);\n    int peek1 = machine_peek(core, a + 1);\n    trackRow.sound = peek1 >> 4;\n    trackRow.volume = peek1 & 0x0F;\n    int peek2 = machine_peek(core, a + 2);\n    trackRow.command = peek2 >> 4;\n    trackRow.parameter = peek2 & 0x0F;\n    return trackRow;\n}\n\nvoid audlib_playRow(struct AudioLib *lib, struct ComposerPlayer *player, int track, int voiceIndex)\n{\n    struct Core *core = lib->core;\n    struct Voice *voice = &core->machine->audioRegisters.voices[voiceIndex];\n    \n    struct TrackRow trackRow = audlib_getTrackRow(lib, player->sourceAddress, track, player->row);\n    if (trackRow.note > 0 && trackRow.note < 255)\n    {\n        audlib_copySound(lib, player->sourceAddress, trackRow.sound, voiceIndex);\n    }\n    if (trackRow.volume > 0)\n    {\n        voice->status.volume = trackRow.volume;\n    }\n    audlib_command(lib, voice, player, trackRow.command, trackRow.parameter);\n    if (trackRow.note == 255)\n    {\n        voice->status.gate = 0;\n    }\n    else if (trackRow.note > 0)\n    {\n        audlib_setPitch(voice, trackRow.note);\n        voice->status.init = 1;\n        voice->status.gate = 1;\n    }\n}\n\nvoid audlib_command(struct AudioLib *lib, struct Voice *voice, struct ComposerPlayer *player, int command, int parameter)\n{\n    if (command == 0 && parameter == 0) return;\n    switch (command)\n    {\n        case 0x00:\n            voice->status.mix = parameter & 0x03;\n            break;\n        case 0x01:\n            voice->envA = parameter;\n            break;\n        case 0x02:\n            voice->envD = parameter;\n            break;\n        case 0x03:\n            voice->envS = parameter;\n            break;\n        case 0x04:\n            voice->envR = parameter;\n            break;\n        case 0x05:\n            voice->lfoFrequency = parameter;\n            break;\n        case 0x06:\n            voice->lfoOscAmount = parameter;\n            break;\n        case 0x07:\n            voice->lfoVolAmount = parameter;\n            break;\n        case 0x08:\n            voice->lfoPWAmount = parameter;\n            break;\n        case 0x09:\n            voice->attr.pulseWidth = parameter;\n            break;\n        case 0x0D:\n            player->speed = 0x10 | parameter;\n            break;\n        case 0x0E:\n            player->speed = parameter;\n            break;\n        case 0x0F:\n            switch (parameter)\n            {\n                case 0:\n                    player->willBreak = true;\n                    break;\n                case 1:\n                    voice->status.gate = 0;\n                    voice->status.volume = 0;\n                    break;\n            }\n            break;\n    }\n}\n"
  },
  {
    "path": "core/libraries/audio_lib.h",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef audio_lib_h\n#define audio_lib_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"audio_chip.h\"\n\n#define NUM_SOUNDS 16\n#define NUM_PATTERNS 64\n#define NUM_TRACKS 64\n#define NUM_TRACK_ROWS 32\n\nstruct Core;\n\nstruct ComposerPlayer {\n    int sourceAddress;\n    int index; // pattern for music, otherwise track\n    int speed;\n    int tick;\n    int row;\n    bool willBreak;\n};\n\nstruct AudioLib {\n    struct Core *core;\n    int sourceAddress;\n    \n    struct ComposerPlayer musicPlayer;\n    struct ComposerPlayer trackPlayers[NUM_VOICES];\n};\n\nvoid audlib_play(struct AudioLib *lib, int voiceIndex, float pitch, int len, int sound);\nvoid audlib_copySound(struct AudioLib *lib, int sourceAddress, int sound, int voiceIndex);\nvoid audlib_playMusic(struct AudioLib *lib, int startPattern);\nvoid audlib_playTrack(struct AudioLib *lib, int track, int voiceIndex);\nvoid audlib_stopAll(struct AudioLib *lib);\nvoid audlib_stopVoice(struct AudioLib *lib, int voiceIndex);\nvoid audlib_update(struct AudioLib *lib);\n\n#endif /* audio_lib_h */\n"
  },
  {
    "path": "core/libraries/default_characters.c",
    "content": "//\n// Copyright 2016 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"default_characters.h\"\n\nuint8_t DefaultCharacters[][16] = {\n    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},\n    {0x00, 0x18, 0x14, 0x04, 0x04, 0x0C, 0x10, 0x0C, 0x00, 0x00, 0x0C, 0x1C, 0x1C, 0x0C, 0x08, 0x0C},\n    {0x00, 0x48, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00, 0x24, 0x7E, 0x36, 0x12, 0x00, 0x00, 0x00},\n    {0x00, 0x24, 0x60, 0x1B, 0x12, 0x40, 0x1B, 0x12, 0x00, 0x00, 0x1E, 0x3F, 0x36, 0x3E, 0x3F, 0x12},\n    {0x00, 0x08, 0x30, 0x27, 0x10, 0x21, 0x17, 0x04, 0x00, 0x00, 0x0E, 0x1F, 0x1E, 0x1F, 0x1F, 0x04},\n    {0x00, 0x40, 0x11, 0x32, 0x04, 0x0C, 0x11, 0x23, 0x00, 0x22, 0x75, 0x3A, 0x14, 0x2A, 0x57, 0x23},\n    {0x00, 0x10, 0x2A, 0x02, 0x10, 0x13, 0x00, 0x1D, 0x00, 0x0C, 0x1E, 0x3A, 0x7E, 0x77, 0x3A, 0x1D},\n    {0x00, 0x10, 0x04, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3C, 0x18, 0x00, 0x00, 0x00},\n    {0x00, 0x08, 0x16, 0x0C, 0x08, 0x00, 0x00, 0x06, 0x00, 0x04, 0x0E, 0x3C, 0x38, 0x18, 0x0C, 0x06},\n    {0x00, 0x30, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x0E, 0x1E, 0x3C, 0x18},\n    {0x00, 0x00, 0x20, 0x12, 0x40, 0x27, 0x08, 0x12, 0x00, 0x00, 0x04, 0x0A, 0x3E, 0x3F, 0x2C, 0x12},\n    {0x00, 0x00, 0x18, 0x14, 0x40, 0x27, 0x04, 0x0C, 0x00, 0x00, 0x00, 0x0C, 0x3E, 0x3F, 0x1C, 0x0C},\n    {0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x3C, 0x18},\n    {0x00, 0x00, 0x00, 0x00, 0x60, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0x00, 0x00},\n    {0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x0C},\n    {0x00, 0x04, 0x0B, 0x06, 0x0C, 0x18, 0x30, 0x20, 0x00, 0x02, 0x07, 0x1E, 0x3C, 0x78, 0x70, 0x20},\n    {0x00, 0x20, 0x58, 0x11, 0x01, 0x19, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x7F, 0x77, 0x7F, 0x3F, 0x1E},\n    {0x00, 0x10, 0x24, 0x04, 0x04, 0x04, 0x40, 0x3F, 0x00, 0x08, 0x1C, 0x1C, 0x1C, 0x1C, 0x3E, 0x3F},\n    {0x00, 0x20, 0x58, 0x33, 0x06, 0x0C, 0x00, 0x3F, 0x00, 0x1C, 0x3E, 0x3F, 0x1E, 0x3C, 0x7E, 0x3F},\n    {0x00, 0x20, 0x58, 0x33, 0x00, 0x41, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x3F, 0x06, 0x27, 0x3F, 0x1E},\n    {0x00, 0x66, 0x55, 0x01, 0x39, 0x01, 0x01, 0x03, 0x00, 0x00, 0x33, 0x7F, 0x3F, 0x07, 0x07, 0x03},\n    {0x00, 0x60, 0x5F, 0x00, 0x38, 0x01, 0x43, 0x3E, 0x00, 0x1E, 0x3F, 0x7C, 0x3E, 0x07, 0x3F, 0x3E},\n    {0x00, 0x10, 0x2E, 0x00, 0x18, 0x11, 0x03, 0x1E, 0x00, 0x0C, 0x1E, 0x7C, 0x7E, 0x77, 0x3F, 0x1E},\n    {0x00, 0x60, 0x39, 0x03, 0x06, 0x0C, 0x08, 0x18, 0x00, 0x1E, 0x3F, 0x0F, 0x1E, 0x3C, 0x38, 0x18},\n    {0x00, 0x20, 0x58, 0x03, 0x18, 0x11, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x3F, 0x7E, 0x77, 0x3F, 0x1E},\n    {0x00, 0x20, 0x58, 0x01, 0x19, 0x41, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x3F, 0x1F, 0x27, 0x3F, 0x1E},\n    {0x00, 0x00, 0x00, 0x10, 0x0C, 0x10, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0C, 0x08, 0x0C, 0x00},\n    {0x00, 0x00, 0x00, 0x10, 0x0C, 0x10, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x08, 0x0C, 0x08, 0x3C, 0x18},\n    {0x00, 0x00, 0x08, 0x16, 0x0C, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x0E, 0x3C, 0x18, 0x0C, 0x06},\n    {0x00, 0x00, 0x00, 0x60, 0x3F, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F, 0x3E, 0x3F, 0x00},\n    {0x00, 0x00, 0x30, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x18, 0x0C, 0x1E, 0x3C, 0x18},\n    {0x00, 0x20, 0x58, 0x33, 0x06, 0x0C, 0x10, 0x0C, 0x00, 0x1C, 0x3E, 0x3F, 0x1E, 0x0C, 0x08, 0x0C},\n    {0x00, 0x20, 0x58, 0x19, 0x11, 0x17, 0x00, 0x1E, 0x00, 0x1C, 0x3E, 0x77, 0x7F, 0x77, 0x3C, 0x1E},\n    {0x00, 0x10, 0x20, 0x18, 0x01, 0x19, 0x11, 0x33, 0x00, 0x08, 0x1C, 0x7E, 0x7F, 0x7F, 0x77, 0x33},\n    {0x00, 0x60, 0x58, 0x03, 0x18, 0x11, 0x03, 0x3E, 0x00, 0x1C, 0x3E, 0x7F, 0x7E, 0x77, 0x7F, 0x3E},\n    {0x00, 0x20, 0x58, 0x13, 0x10, 0x10, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x73, 0x70, 0x76, 0x3F, 0x1E},\n    {0x00, 0x60, 0x50, 0x10, 0x11, 0x13, 0x06, 0x3C, 0x00, 0x18, 0x3C, 0x76, 0x77, 0x7F, 0x7E, 0x3C},\n    {0x00, 0x60, 0x5F, 0x00, 0x1C, 0x10, 0x00, 0x3F, 0x00, 0x1E, 0x3F, 0x78, 0x7C, 0x70, 0x7E, 0x3F},\n    {0x00, 0x60, 0x5F, 0x00, 0x1C, 0x10, 0x10, 0x30, 0x00, 0x1E, 0x3F, 0x78, 0x7C, 0x70, 0x70, 0x30},\n    {0x00, 0x20, 0x5E, 0x1C, 0x11, 0x11, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x72, 0x77, 0x77, 0x3F, 0x1E},\n    {0x00, 0x66, 0x55, 0x01, 0x19, 0x11, 0x11, 0x33, 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x77, 0x77, 0x33},\n    {0x00, 0x30, 0x06, 0x04, 0x04, 0x04, 0x00, 0x1E, 0x00, 0x0C, 0x1E, 0x1C, 0x1C, 0x1C, 0x3C, 0x1E},\n    {0x00, 0x1C, 0x09, 0x01, 0x01, 0x41, 0x03, 0x1E, 0x00, 0x02, 0x0F, 0x07, 0x07, 0x27, 0x3F, 0x1E},\n    {0x00, 0x64, 0x5B, 0x06, 0x04, 0x10, 0x10, 0x33, 0x00, 0x02, 0x37, 0x7E, 0x7C, 0x7C, 0x76, 0x33},\n    {0x00, 0x60, 0x50, 0x10, 0x10, 0x10, 0x00, 0x3F, 0x00, 0x00, 0x30, 0x70, 0x70, 0x70, 0x7E, 0x3F},\n    {0x00, 0x42, 0x45, 0x09, 0x01, 0x19, 0x11, 0x33, 0x00, 0x00, 0x23, 0x77, 0x7F, 0x7F, 0x77, 0x33},\n    {0x00, 0x66, 0x45, 0x01, 0x11, 0x11, 0x11, 0x33, 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x77, 0x77, 0x33},\n    {0x00, 0x20, 0x58, 0x11, 0x11, 0x11, 0x03, 0x1E, 0x00, 0x1C, 0x3E, 0x77, 0x77, 0x77, 0x3F, 0x1E},\n    {0x00, 0x60, 0x58, 0x03, 0x1E, 0x10, 0x10, 0x30, 0x00, 0x1C, 0x3E, 0x7F, 0x7E, 0x70, 0x70, 0x30},\n    {0x00, 0x20, 0x58, 0x11, 0x11, 0x11, 0x00, 0x1F, 0x00, 0x1C, 0x3E, 0x77, 0x7B, 0x7D, 0x3E, 0x1F},\n    {0x00, 0x60, 0x58, 0x03, 0x06, 0x10, 0x10, 0x33, 0x00, 0x1C, 0x3E, 0x7F, 0x7E, 0x7C, 0x76, 0x33},\n    {0x00, 0x20, 0x5F, 0x00, 0x18, 0x01, 0x03, 0x3E, 0x00, 0x1E, 0x3F, 0x3C, 0x1E, 0x07, 0x7F, 0x3E},\n    {0x00, 0x70, 0x27, 0x04, 0x04, 0x04, 0x04, 0x0C, 0x00, 0x0E, 0x3F, 0x1C, 0x1C, 0x1C, 0x1C, 0x0C},\n    {0x00, 0x66, 0x55, 0x11, 0x11, 0x11, 0x03, 0x1E, 0x00, 0x00, 0x33, 0x77, 0x77, 0x77, 0x3F, 0x1E},\n    {0x00, 0x66, 0x55, 0x11, 0x11, 0x03, 0x06, 0x0C, 0x00, 0x00, 0x33, 0x77, 0x77, 0x3F, 0x1E, 0x0C},\n    {0x00, 0x66, 0x55, 0x15, 0x01, 0x19, 0x31, 0x21, 0x00, 0x00, 0x33, 0x6B, 0x7F, 0x7F, 0x73, 0x21},\n    {0x00, 0x64, 0x0B, 0x06, 0x00, 0x18, 0x11, 0x33, 0x00, 0x02, 0x37, 0x1E, 0x3C, 0x7E, 0x77, 0x33},\n    {0x00, 0x66, 0x55, 0x0B, 0x06, 0x04, 0x04, 0x0C, 0x00, 0x00, 0x33, 0x37, 0x1E, 0x1C, 0x1C, 0x0C},\n    {0x00, 0x60, 0x33, 0x06, 0x0C, 0x58, 0x00, 0x3F, 0x00, 0x1E, 0x3F, 0x1E, 0x3C, 0x38, 0x7E, 0x3F},\n    {0x00, 0x30, 0x2E, 0x08, 0x08, 0x08, 0x00, 0x1E, 0x00, 0x0C, 0x1E, 0x38, 0x38, 0x38, 0x3C, 0x1E},\n    {0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01},\n    {0x00, 0x38, 0x12, 0x02, 0x02, 0x02, 0x02, 0x1E, 0x00, 0x04, 0x1E, 0x0E, 0x0E, 0x0E, 0x3E, 0x1E},\n    {0x00, 0x10, 0x20, 0x18, 0x33, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1C, 0x7E, 0x33, 0x00, 0x00, 0x00},\n    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x3F},\n};\n"
  },
  {
    "path": "core/libraries/default_characters.h",
    "content": "//\n// Copyright 2016 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef default_characters_h\n#define default_characters_h\n\n#include <stdio.h>\n#include <stdint.h>\n\nextern uint8_t DefaultCharacters[][16];\n\n#endif /* default_characters_h */\n"
  },
  {
    "path": "core/libraries/sprites_lib.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"sprites_lib.h\"\n#include \"core.h\"\n#include <stdint.h>\n\nbool sprlib_isSpriteOnScreen(struct Sprite *sprite)\n{\n    int size = (sprite->attr.size + 1) << 3;\n    return (   sprite->x < SCREEN_WIDTH + SPRITE_OFFSET_X\n            && sprite->y < SCREEN_HEIGHT + SPRITE_OFFSET_Y\n            && sprite->x + size > SPRITE_OFFSET_X\n            && sprite->y + size > SPRITE_OFFSET_Y);\n}\n\nbool sprlib_checkSingleCollision(struct SpritesLib *lib, struct Sprite *sprite, struct Sprite *otherSprite)\n{\n    if (sprlib_isSpriteOnScreen(otherSprite))\n    {\n        int ax1 = sprite->x;\n        int ay1 = sprite->y;\n        \n        int ax2 = otherSprite->x;\n        int ay2 = otherSprite->y;\n        \n        int s1 = (sprite->attr.size + 1) << 3;\n        int s2 = (otherSprite->attr.size + 1) << 3;\n        \n        int bx1 = ax1 + s1;\n        int by1 = ay1 + s1;\n        int bx2 = ax2 + s2;\n        int by2 = ay2 + s2;\n        \n        // rectangle check\n        if (bx1 > ax2 && by1 > ay2 && ax1 < bx2 && ay1 < by2)\n        {\n            // pixel exact check\n            int diffX = ax2 - ax1;\n            int diffY = ay2 - ay1;\n            \n            struct Character *characters = lib->core->machine->videoRam.characters;\n            int c1 = sprite->character;\n            int c2 = otherSprite->character;\n            \n            for (int line = 0; line < s1; line++)\n            {\n                if (line - diffY >= 0 && line - diffY < s2)\n                {\n                    int line1 = sprite->attr.flipY ? (s1 - line - 1) : line;\n                    int line2 = otherSprite->attr.flipY ? (s2 - (line - diffY) - 1) : (line - diffY);\n                    bool flx1 = sprite->attr.flipX;\n                    bool flx2 = otherSprite->attr.flipX;\n                    \n                    uint32_t source1 = 0;\n                    int chLine1 = line1 & 7;\n                    int rc1 = c1 + (line1 >> 3 << 4);\n                    for (int i = 0; i <= sprite->attr.size; i++)\n                    {\n                        uint8_t *data = characters[flx1 ? (rc1 + sprite->attr.size - i) : (rc1 + i)].data;\n                        uint32_t val = (data[chLine1] | data[chLine1 + 8]);\n                        if (flx1)\n                        {\n                            // reverse bits\n                            val = (((val * 0x0802LU & 0x22110LU) | (val * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) & 0xFF;\n                        }\n                        source1 |= val << (24 - (i << 3));\n                    }\n                    \n                    uint32_t source2 = 0;\n                    int chLine2 = line2 & 7;\n                    int rc2 = c2 + (line2 >> 3 << 4);\n                    for (int i = 0; i <= otherSprite->attr.size; i++)\n                    {\n                        uint8_t *data = characters[flx2 ? (rc2 + otherSprite->attr.size - i) : (rc2 + i)].data;\n                        uint32_t val = (data[chLine2] | data[chLine2 + 8]);\n                        if (flx2)\n                        {\n                            // reverse bits\n                            val = (((val * 0x0802LU & 0x22110LU) | (val * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16) & 0xFF;\n                        }\n                        \n                        int shift = (24 - (i << 3) - diffX);\n                        if (shift >= 0 && shift < 32)\n                        {\n                            source2 |= val << shift;\n                        }\n                        else if (shift > -32 && shift < 0)\n                        {\n                            source2 |= val >> -shift;\n                        }\n                    }\n                    \n                    if (source1 & source2)\n                    {\n                        return true;\n                    }\n                }\n            }\n        }\n    }\n    return false;\n}\n\nbool sprlib_checkCollision(struct SpritesLib *lib, int checkIndex, int firstIndex, int lastIndex)\n{\n    struct Sprite *sprites = lib->core->machine->spriteRegisters.sprites;\n    struct Sprite *sprite = &sprites[checkIndex];\n    \n    if (sprlib_isSpriteOnScreen(sprite))\n    {\n        for (int i = firstIndex; i <= lastIndex; i++)\n        {\n            if (i != checkIndex)\n            {\n                if (sprlib_checkSingleCollision(lib, sprite, &sprites[i]))\n                {\n                    lib->lastHit = i;\n                    return true;\n                }\n            }\n        }\n    }\n    return false;\n}\n"
  },
  {
    "path": "core/libraries/sprites_lib.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef sprites_lib_h\n#define sprites_lib_h\n\n#include <stdio.h>\n#include <stdbool.h>\n\nstruct Core;\nstruct Sprite;\n\nstruct SpritesLib {\n    struct Core *core;\n    int lastHit;\n};\n\nbool sprlib_isSpriteOnScreen(struct Sprite *sprite);\nbool sprlib_checkCollision(struct SpritesLib *lib, int checkIndex, int firstIndex, int lastIndex);\n\n#endif /* sprites_lib_h */\n"
  },
  {
    "path": "core/libraries/startup_sequence.c",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"startup_sequence.h\"\n#include \"core.h\"\n#include <string.h>\n#include <stdint.h>\n\n#define FONT_CHAR_OFFSET 192\n\nvoid runStartupSequence(struct Core *core)\n{\n    struct DataEntry *entries = core->interpreter->romDataManager.entries;\n    \n    // init font and window\n    struct TextLib *textLib = &core->interpreter->textLib;\n    textLib->fontCharOffset = FONT_CHAR_OFFSET;\n    txtlib_clearScreen(textLib);\n    \n    // default characters/font\n    if (strcmp(entries[0].comment, \"FONT\") == 0)\n    {\n        memcpy(&core->machine->videoRam.characters[FONT_CHAR_OFFSET], &core->machine->cartridgeRom[entries[0].start], entries[0].length);\n    }\n    \n    // default palettes\n    uint8_t *colors = core->machine->colorRegisters.colors;\n    \n    colors[0] = (0 << 4) | (1 << 2) | 1;\n    colors[1] = (3 << 4) | (3 << 2) | 3;\n    colors[2] = (2 << 4) | (3 << 2) | 3;\n    colors[3] = (0 << 4) | (0 << 2) | 0;\n\n    colors[4] = 0;\n    colors[5] = (3 << 4) | (2 << 2) | 0;\n    colors[6] = (3 << 4) | (1 << 2) | 0;\n    colors[7] = (0 << 4) | (0 << 2) | 0;\n    \n    colors[8] = 0;\n    colors[9] = (3 << 4) | (3 << 2) | 0;\n    colors[10] = (0 << 4) | (3 << 2) | 0;\n    colors[11] = (0 << 4) | (0 << 2) | 0;\n\n    colors[12] = 0;\n    colors[13] = (3 << 4) | (3 << 2) | 3;\n    colors[14] = (3 << 4) | (3 << 2) | 0;\n    colors[15] = (0 << 4) | (0 << 2) | 0;\n    \n    for (int i = 0; i < 16; i += 4)\n    {\n        colors[16 + i] = 0;\n        colors[17 + i] = (3 << 4) | (3 << 2) | 3;\n        colors[18 + i] = (2 << 4) | (2 << 2) | 2;\n        colors[19 + i] = (1 << 4) | (1 << 2) | 1;\n    }\n    \n    // main palettes\n    int palLen = entries[1].length;\n    if (palLen > 32) palLen = 32;\n    memcpy(core->machine->colorRegisters.colors, &core->machine->cartridgeRom[entries[1].start], palLen);\n    \n    // main characters\n    memcpy(core->machine->videoRam.characters, &core->machine->cartridgeRom[entries[2].start], entries[2].length);\n\n    // main background source\n    int bgStart = entries[3].start;\n    core->interpreter->textLib.sourceAddress = bgStart + 4;\n    core->interpreter->textLib.sourceWidth = core->machine->cartridgeRom[bgStart + 2];\n    core->interpreter->textLib.sourceHeight = core->machine->cartridgeRom[bgStart + 3];\n    \n    // voices\n    for (int i = 0; i < NUM_VOICES; i++)\n    {\n        struct Voice *voice = &core->machine->audioRegisters.voices[i];\n        voice->attr.pulseWidth = 8;\n        voice->status.volume = 15;\n        voice->status.mix = 3;\n        voice->envS = 15;\n    }\n    \n    // main sound source\n    core->interpreter->audioLib.sourceAddress = entries[15].start;\n}\n"
  },
  {
    "path": "core/libraries/startup_sequence.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef startup_sequence_h\n#define startup_sequence_h\n\n#include <stdio.h>\n\nstruct Core;\n\nvoid runStartupSequence(struct Core *core);\n\n#endif /* startup_sequence_h */\n"
  },
  {
    "path": "core/libraries/text_lib.c",
    "content": "//\n// Copyright 2016 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"text_lib.h\"\n#include \"core.h\"\n#include <string.h>\n#include <assert.h>\n\nstruct Plane *txtlib_getBackground(struct TextLib *lib, int bg)\n{\n    switch (bg)\n    {\n        case 0:\n            return &lib->core->machine->videoRam.planeA;\n            \n        case 1:\n            return &lib->core->machine->videoRam.planeB;\n            \n        case OVERLAY_BG:\n            return &lib->core->overlay->plane;\n            \n        default:\n            assert(0);\n            return NULL;\n    }\n}\n\nvoid txtlib_setCellAt(struct Plane *plane, int x, int y, int character, union CharacterAttributes attr)\n{\n    struct Cell *cell = &plane->cells[y & 0x1F][x & 0x1F];\n    if (character >= 0)\n    {\n        cell->character = character;\n    }\n    cell->attr = attr;\n}\n\nvoid txtlib_scrollRow(struct Plane *plane, int fromX, int toX, int y, int deltaX, int deltaY)\n{\n    if (deltaX > 0)\n    {\n        for (int x = toX; x > fromX; x--)\n        {\n            plane->cells[y][x] = plane->cells[(y - deltaY) & 0x1F][(x - deltaX) & 0x1F];\n        }\n    }\n    else if (deltaX < 0)\n    {\n        for (int x = fromX; x < toX; x++)\n        {\n            plane->cells[y][x] = plane->cells[(y - deltaY) & 0x1F][(x - deltaX) & 0x1F];\n        }\n    }\n    else\n    {\n        for (int x = fromX; x <= toX; x++)\n        {\n            plane->cells[y][x] = plane->cells[(y - deltaY) & 0x1F][(x - deltaX) & 0x1F];\n        }\n    }\n}\n\nvoid txtlib_scroll(struct Plane *plane, int fromX, int fromY, int toX, int toY, int deltaX, int deltaY)\n{\n    if (deltaY > 0)\n    {\n        for (int y = toY; y > fromY; y--)\n        {\n            txtlib_scrollRow(plane, fromX, toX, y, deltaX, deltaY);\n        }\n    }\n    else if (deltaY < 0)\n    {\n        for (int y = fromY; y < toY; y++)\n        {\n            txtlib_scrollRow(plane, fromX, toX, y, deltaX, deltaY);\n        }\n    }\n    else\n    {\n        for (int y = fromY; y <= toY; y++)\n        {\n            txtlib_scrollRow(plane, fromX, toX, y, deltaX, deltaY);\n        }\n    }\n}\n\nvoid txtlib_scrollWindowIfNeeded(struct TextLib *lib)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->windowBg);\n    \n    if (lib->cursorY >= lib->windowHeight)\n    {\n        // scroll\n        txtlib_scroll(plane, lib->windowX, lib->windowY, lib->windowX + lib->windowWidth - 1, lib->windowY + lib->windowHeight - 1, 0, -1);\n        \n        // clear bottom line\n        int py = lib->windowY + lib->windowHeight - 1;\n        for (int x = 0; x < lib->windowWidth; x++)\n        {\n            int px = x + lib->windowX;\n            txtlib_setCellAt(plane, px, py, lib->fontCharOffset, lib->charAttr); // space\n        }\n        \n        lib->cursorY = lib->windowHeight - 1;\n        \n        struct Interpreter *interpreter = lib->core->interpreter;\n        if (interpreter->state == StateEvaluate && lib->windowBg != OVERLAY_BG)\n        {\n            interpreter->waitCount = 1;\n            interpreter->exitEvaluation = true;\n            interpreter->cycles += lib->windowWidth * lib->windowHeight * 2;\n        }\n    }\n}\n\nvoid txtlib_printText(struct TextLib *lib, const char *text)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->windowBg);\n    const char *letter = text;\n    while (*letter)\n    {\n        txtlib_scrollWindowIfNeeded(lib);\n        \n        if (*letter >= 32)\n        {\n            char printableLetter = *letter;\n            if (printableLetter >= 'a' && printableLetter <= 'z')\n            {\n                printableLetter -= 32;\n            }\n            txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset + (printableLetter - 32), lib->charAttr);\n            if (lib->windowBg != OVERLAY_BG)\n            {\n                lib->core->interpreter->cycles += 2;\n            }\n            lib->cursorX++;\n        }\n        else if (*letter == '\\n')\n        {\n            lib->cursorX = 0;\n            lib->cursorY++;\n        }\n        \n        if (lib->cursorX >= lib->windowWidth)\n        {\n            lib->cursorX = 0;\n            lib->cursorY++;\n        }\n        \n        letter++;\n    }\n}\n\nbool txtlib_deleteBackward(struct TextLib *lib)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->windowBg);\n    \n    // clear cursor\n    txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset, lib->charAttr);\n    \n    // move back cursor\n    if (lib->cursorX > 0)\n    {\n        lib->cursorX--;\n    }\n    else if (lib->cursorY > 0)\n    {\n        lib->cursorX = lib->windowX + lib->windowWidth - 1;\n        lib->cursorY--;\n    }\n    else\n    {\n        return false;\n    }\n    \n    // clear cell\n    txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset, lib->charAttr);\n    \n    lib->core->interpreter->cycles += 4;\n    return true;\n}\n\nvoid txtlib_writeText(struct TextLib *lib, const char *text, int x, int y)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->bg);\n    const char *letter = text;\n    while (*letter)\n    {\n        if (*letter >= 32)\n        {\n            char printableLetter = *letter;\n            if (printableLetter >= 'a' && printableLetter <= 'z')\n            {\n                printableLetter -= 32;\n            }\n            txtlib_setCellAt(plane, x, y, lib->fontCharOffset + (printableLetter - 32), lib->charAttr);\n            if (lib->windowBg != OVERLAY_BG)\n            {\n                lib->core->interpreter->cycles += 2;\n            }\n            x++;\n        }\n        letter++;\n    }\n}\n\nvoid txtlib_writeNumber(struct TextLib *lib, int number, int digits, int x, int y)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->bg);\n    \n    if (number < 0)\n    {\n        // negative number\n        number *= -1;\n        txtlib_setCellAt(plane, x, y, lib->fontCharOffset + 13, lib->charAttr); // \"-\"\n        x += digits;\n        digits--;\n    }\n    else\n    {\n        x += digits;\n    }\n    \n    int div = 1;\n    for (int i = 0; i < digits; i++)\n    {\n        x--;\n        txtlib_setCellAt(plane, x, y, lib->fontCharOffset + ((number / div) % 10 + 16), lib->charAttr);\n        div *= 10;\n    }\n    \n    if (lib->windowBg != OVERLAY_BG)\n    {\n        lib->core->interpreter->cycles += digits * 2;\n    }\n}\n\nvoid txtlib_inputBegin(struct TextLib *lib)\n{\n    lib->inputBuffer[0] = 0;\n    lib->inputLength = 0;\n    lib->blink = 0;\n    lib->core->machine->ioRegisters.key = 0;\n    \n    lib->core->machine->ioRegisters.attr.keyboardEnabled = 1;\n    lib->core->interpreter->isKeyboardOptional = false;\n    delegate_controlsDidChange(lib->core);\n    \n    txtlib_scrollWindowIfNeeded(lib);\n}\n\nbool txtlib_inputUpdate(struct TextLib *lib)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->windowBg);\n    \n    char key = lib->core->machine->ioRegisters.key;\n    bool done = false;\n    if (key)\n    {\n        if (key == CoreInputKeyBackspace)\n        {\n            if (lib->inputLength > 0)\n            {\n                if (txtlib_deleteBackward(lib))\n                {\n                    lib->inputBuffer[--lib->inputLength] = 0;\n                }\n            }\n        }\n        else if (key == CoreInputKeyReturn)\n        {\n            // clear cursor\n            txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset, lib->charAttr);\n            txtlib_printText(lib, \"\\n\");\n            done = true;\n        }\n        else if (key >= 32)\n        {\n            if (lib->inputLength < INPUT_BUFFER_SIZE - 2)\n            {\n                char text[2] = {key, 0};\n                txtlib_printText(lib, text);\n                lib->inputBuffer[lib->inputLength++] = key;\n                lib->inputBuffer[lib->inputLength] = 0;\n                \n                txtlib_scrollWindowIfNeeded(lib);\n            }\n        }\n        lib->blink = 0;\n        lib->core->machine->ioRegisters.key = 0;\n    }\n    if (!done)\n    {\n        txtlib_setCellAt(plane, lib->cursorX + lib->windowX, lib->cursorY + lib->windowY, lib->fontCharOffset + (lib->blink++ < 30 ? 63 : 0), lib->charAttr);\n        if (lib->blink == 60)\n        {\n            lib->blink = 0;\n        }\n    }\n    return done;\n}\n\nvoid txtlib_clearWindow(struct TextLib *lib)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->windowBg);\n    \n    lib->cursorX = 0;\n    lib->cursorY = 0;\n    for (int y = 0; y < lib->windowHeight; y++)\n    {\n        int py = y + lib->windowY;\n        for (int x = 0; x < lib->windowWidth; x++)\n        {\n            int px = x + lib->windowX;\n            txtlib_setCellAt(plane, px, py, lib->fontCharOffset, lib->charAttr);\n        }\n    }\n    lib->core->interpreter->cycles += lib->windowWidth * lib->windowHeight * 2;\n}\n\nvoid txtlib_clearScreen(struct TextLib *lib)\n{\n    struct VideoRegisters *reg = &lib->core->machine->videoRegisters;\n    \n    memset(&lib->core->machine->videoRam.planeA, 0, sizeof(struct Plane));\n    memset(&lib->core->machine->videoRam.planeB, 0, sizeof(struct Plane));\n    \n    reg->scrollAX = 0;\n    reg->scrollAY = 0;\n    reg->scrollBX = 0;\n    reg->scrollBY = 0;\n    reg->attr.spritesEnabled = 1;\n    reg->attr.planeAEnabled = 1;\n    reg->attr.planeBEnabled = 1;\n    \n    lib->windowX = 0;\n    lib->windowY = 0;\n    lib->windowWidth = 20;\n    lib->windowHeight = 16;\n    lib->cursorX = 0;\n    lib->cursorY = 0;\n    lib->bg = 0;\n    \n    lib->core->interpreter->cycles += PLANE_COLUMNS * PLANE_ROWS * 2 * 2;\n}\n\nvoid txtlib_clearBackground(struct TextLib *lib, int bg)\n{\n    struct Plane *plane = txtlib_getBackground(lib, bg);\n    memset(plane, 0, sizeof(struct Plane));\n    lib->core->interpreter->cycles += PLANE_COLUMNS * PLANE_ROWS * 2;\n}\n\nstruct Cell *txtlib_getCell(struct TextLib *lib, int x, int y)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->bg);\n    return &plane->cells[y & 0x1F][x & 0x1F];\n}\n\nvoid txtlib_setCell(struct TextLib *lib, int x, int y, int character)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->bg);\n    txtlib_setCellAt(plane, x, y, character, lib->charAttr);\n}\n\nvoid txtlib_setCells(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int character)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->bg);\n    for (int y = fromY; y <= toY; y++)\n    {\n        for (int x = fromX; x <= toX; x++)\n        {\n            txtlib_setCellAt(plane, x, y, character, lib->charAttr);\n        }\n    }\n    lib->core->interpreter->cycles += (toX - fromX + 1) * (toY - fromY + 1) * 2;\n}\n\nvoid txtlib_setCellsAttr(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int pal, int flipX, int flipY, int prio)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->bg);\n    for (int y = fromY; y <= toY; y++)\n    {\n        for (int x = fromX; x <= toX; x++)\n        {\n            struct Cell *cell = &plane->cells[y & 0x1F][x & 0x1F];\n            if (pal >= 0) cell->attr.palette = pal;\n            if (flipX >= 0) cell->attr.flipX = flipX;\n            if (flipY >= 0) cell->attr.flipY = flipY;\n            if (prio >= 0) cell->attr.priority = prio;\n        }\n    }\n    lib->core->interpreter->cycles += (toX - fromX + 1) * (toY - fromY + 1) * 2;\n}\n\nvoid txtlib_scrollBackground(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int deltaX, int deltaY)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->bg);\n    txtlib_scroll(plane, fromX, fromY, toX, toY, deltaX, deltaY);\n    lib->core->interpreter->cycles += (toX - fromX + 1) * (toY - fromY + 1) * 2;\n}\n\nvoid txtlib_copyBackground(struct TextLib *lib, int srcX, int srcY, int width, int height, int dstX, int dstY)\n{\n    struct Plane *plane = txtlib_getBackground(lib, lib->bg);\n    \n    for (int y = 0; y < height; y++)\n    {\n        int py = dstY + y;\n        int addr = lib->sourceAddress + ((srcY + y) * lib->sourceWidth + srcX) * 2;\n        for (int x = 0; x < width; x++)\n        {\n            int px = dstX + x;\n            struct Cell *cell = &plane->cells[py & 0x1F][px & 0x1F];\n            cell->character = machine_peek(lib->core, addr++);\n            cell->attr.value = machine_peek(lib->core, addr++);\n        }\n    }\n    lib->core->interpreter->cycles += width * height * 2;\n}\n\nint txtlib_getSourceCell(struct TextLib *lib, int x, int y, bool getAttrs)\n{\n    if (x >= 0 && y >= 0 && x < lib->sourceWidth && y < lib->sourceHeight)\n    {\n        int address = lib->sourceAddress + ((y * lib->sourceWidth) + x) * 2;\n        if (getAttrs)\n        {\n            return machine_peek(lib->core, address + 1);\n        }\n        else\n        {\n            return machine_peek(lib->core, address);\n        }\n    }\n    return -1;\n}\n\nbool txtlib_setSourceCell(struct TextLib *lib, int x, int y, int character)\n{\n    int address = lib->sourceAddress + ((y * lib->sourceWidth) + x) * 2;\n    // only working RAM is allowed\n    if (address < 0xA000 || address + 1 >= 0xE000)\n    {\n        return false;\n    }\n    \n    if (character >= 0)\n    {\n        machine_poke(lib->core, address, character);\n    }\n    machine_poke(lib->core, address + 1, lib->charAttr.value);\n    return true;\n}\n\nvoid txtlib_itobin(char *buffer, size_t buffersize, size_t width, int value)\n{\n    if (width < 1)\n    {\n        width = 1;\n    }\n    unsigned int mask = 1 << 15;\n    int p = 0;\n    bool active = false;\n    while (mask && p < buffersize - 1)\n    {\n        if (active || (value & mask) || mask < (1 << width))\n        {\n            buffer[p++] = (value & mask) ? '1' : '0';\n            active = true;\n        }\n        mask = (mask >> 1) & 0x7FFF;\n    }\n    buffer[p] = 0;\n}\n"
  },
  {
    "path": "core/libraries/text_lib.h",
    "content": "//\n// Copyright 2016-2019 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef text_lib_h\n#define text_lib_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include \"video_chip.h\"\n\n#define INPUT_BUFFER_SIZE 256\n#define OVERLAY_BG 2\n\nstruct Core;\n\nstruct TextLib {\n    struct Core *core;\n    union CharacterAttributes charAttr;\n    int fontCharOffset;\n    int windowX;\n    int windowY;\n    int windowWidth;\n    int windowHeight;\n    int windowBg;\n    int cursorX;\n    int cursorY;\n    int bg;\n    int sourceAddress;\n    int sourceWidth;\n    int sourceHeight;\n    char inputBuffer[INPUT_BUFFER_SIZE];\n    int inputLength;\n    int blink;\n};\n\nvoid txtlib_printText(struct TextLib *lib, const char *text);\nbool txtlib_deleteBackward(struct TextLib *lib);\nvoid txtlib_writeText(struct TextLib *lib, const char *text, int x, int y);\nvoid txtlib_writeNumber(struct TextLib *lib, int number, int digits, int x, int y);\nvoid txtlib_inputBegin(struct TextLib *lib);\nbool txtlib_inputUpdate(struct TextLib *lib);\nvoid txtlib_clearWindow(struct TextLib *lib);\nvoid txtlib_clearScreen(struct TextLib *lib);\nvoid txtlib_clearBackground(struct TextLib *lib, int bg);\nstruct Cell *txtlib_getCell(struct TextLib *lib, int x, int y);\nvoid txtlib_setCell(struct TextLib *lib, int x, int y, int character);\nvoid txtlib_setCells(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int character);\nvoid txtlib_setCellsAttr(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int pal, int flipX, int flipY, int prio);\nvoid txtlib_scrollBackground(struct TextLib *lib, int fromX, int fromY, int toX, int toY, int deltaX, int deltaY);\nvoid txtlib_copyBackground(struct TextLib *lib, int srcX, int srcY, int width, int height, int dstX, int dstY);\nint txtlib_getSourceCell(struct TextLib *lib, int x, int y, bool getAttrs);\nbool txtlib_setSourceCell(struct TextLib *lib, int x, int y, int character);\n\nvoid txtlib_itobin(char *buffer, size_t buffersize, size_t width, int value);\n\n#endif /* text_lib_h */\n"
  },
  {
    "path": "core/machine/audio_chip.c",
    "content": "//\n// Copyright 2016-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"audio_chip.h\"\n#include \"core.h\"\n#include <math.h>\n#include <string.h>\n\nconst double envRates[16] = {\n    256.0 / 0.002,\n    256.0 / 0.03,\n    256.0 / 0.06,\n    256.0 / 0.09,\n    256.0 / 0.14,\n    256.0 / 0.21,\n    256.0 / 0.31,\n    256.0 / 0.47,\n    256.0 / 0.70,\n    256.0 / 1.0,\n    256.0 / 1.6,\n    256.0 / 2.4,\n    256.0 / 3.5,\n    256.0 / 5.0,\n    256.0 / 8.0,\n    256.0 / 12.0\n};\n\nconst double lfoRates[16] = {\n    0.12 * 256.0,\n    0.16 * 256.0,\n    0.23 * 256.0,\n    0.32 * 256.0,\n    0.44 * 256.0,\n    0.62 * 256.0,\n    0.87 * 256.0,\n    1.2 * 256.0,\n    1.7 * 256.0,\n    2.4 * 256.0,\n    3.3 * 256.0,\n    4.7 * 256.0,\n    6.6 * 256.0,\n    9.2 * 256.0,\n    12.9 * 256.0,\n    18.0 * 256.0\n};\n\nconst int lfoAmounts[16] = {\n    0,\n    1,\n    2,\n    4,\n    6,\n    9,\n    12,\n    17,\n    24,\n    34,\n    48,\n    67,\n    93,\n    131,\n    183,\n    256\n};\n\nvoid audio_renderAudioBuffer(struct AudioRegisters *lifeRegisters, struct AudioRegisters *registers, struct AudioInternals *internals, int16_t *stereoOutput, int numSamples, int outputFrequency, int volume);\n\n\nvoid audio_reset(struct Core *core)\n{\n    struct AudioInternals *internals = &core->machineInternals->audioInternals;\n    \n    for (int i = 0; i < NUM_VOICES; i++)\n    {\n        struct VoiceInternals *voiceIn = &internals->voices[i];\n        voiceIn->noiseRandom = 0xABCD;\n        voiceIn->lfoRandom = 0xABCD;\n    }\n    internals->writeBufferIndex = -1;\n}\n\nvoid audio_bufferRegisters(struct Core *core)\n{\n    struct AudioRegisters *registers = &core->machine->audioRegisters;\n    struct AudioInternals *internals = &core->machineInternals->audioInternals;\n    \n    // next buffer\n    int writeBufferIndex = internals->writeBufferIndex;\n    if (writeBufferIndex >= 0)\n    {\n        writeBufferIndex = (writeBufferIndex + 1) % NUM_AUDIO_BUFFERS;\n    }\n    else\n    {\n        writeBufferIndex = NUM_AUDIO_BUFFERS / 2;\n    }\n    \n    // copy registers to buffer\n    memcpy(&internals->buffers[writeBufferIndex], registers, sizeof(struct AudioRegisters));\n    \n    // reset \"init\" flags\n    for (int v = 0; v < NUM_VOICES; v++)\n    {\n        struct Voice *voice = &registers->voices[v];\n        voice->status.init = 0;\n    }\n    \n    internals->writeBufferIndex = writeBufferIndex;\n}\n\nvoid audio_renderAudio(struct Core *core, int16_t *stereoOutput, int numSamples, int outputFrequency, int volume)\n{\n    struct AudioInternals *internals = &core->machineInternals->audioInternals;\n    struct AudioRegisters *lifeRegisters = &core->machine->audioRegisters;\n    \n    int numSamplesPerUpdate = outputFrequency / 60 * NUM_CHANNELS;\n    int offset = 0;\n    \n    while (offset < numSamples)\n    {\n        if (offset + numSamplesPerUpdate > numSamples)\n        {\n            numSamplesPerUpdate = numSamples - offset;\n        }\n        int readBufferIndex = internals->readBufferIndex;\n        audio_renderAudioBuffer(lifeRegisters, &internals->buffers[readBufferIndex], internals, &stereoOutput[offset], numSamplesPerUpdate, outputFrequency, volume);\n        if (internals->writeBufferIndex != -1 && internals->writeBufferIndex != readBufferIndex)\n        {\n            internals->readBufferIndex = (readBufferIndex + 1) % NUM_AUDIO_BUFFERS;\n        }\n        \n        offset += numSamplesPerUpdate;\n    }\n}\n\nvoid audio_renderAudioBuffer(struct AudioRegisters *lifeRegisters, struct AudioRegisters *registers, struct AudioInternals *internals, int16_t *stereoOutput, int numSamples, int outputFrequency, int volume)\n{\n    double overflow = 0xFFFFFF;\n    \n    for (int v = 0; v < NUM_VOICES; v++)\n    {\n        struct Voice *voice = &registers->voices[v];\n        if (voice->status.init)\n        {\n            voice->status.init = 0;\n            \n            struct VoiceInternals *voiceIn = &internals->voices[v];\n            voiceIn->envState = EnvStateAttack;\n            voiceIn->lfoHold = false;\n            voiceIn->timeoutCounter = voice->length;\n            if (voice->lfoAttr.envMode || voice->lfoAttr.trigger)\n            {\n                voiceIn->lfoAccumulator = 0.0;\n            }\n        }\n    }\n    \n    int i = 0;\n    while (i < numSamples)\n    {\n        int16_t leftOutput = 0;\n        int16_t rightOutput = 0;\n        \n        if (internals->audioEnabled)\n        {\n            for (int v = 0; v < NUM_VOICES; v++)\n            {\n                struct Voice *voice = &registers->voices[v];\n                struct VoiceInternals *voiceIn = &internals->voices[v];\n                \n                int freq = (voice->frequencyHigh << 8) | voice->frequencyLow;\n                if (freq == 0) continue;\n                \n                int volume = voice->status.volume << 4;\n                int pulseWidth = voice->attr.pulseWidth << 4;\n                \n                // --- LFO ---\n                \n                uint8_t lfoAccu8Last = voiceIn->lfoAccumulator;\n                if (!voiceIn->lfoHold)\n                {\n                    double lfoRate = lfoRates[voice->lfoFrequency];\n                    double lfoAccumulator = voiceIn->lfoAccumulator + lfoRate / (double)outputFrequency;\n                    if (voice->lfoAttr.envMode && lfoAccumulator >= 255.0)\n                    {\n                        lfoAccumulator = 255.0;\n                        voiceIn->lfoHold = true;\n                    }\n                    else if (lfoAccumulator >= 256.0)\n                    {\n                        // avoid overflow and loss of precision\n                        lfoAccumulator -= 256.0;\n                    }\n                    voiceIn->lfoAccumulator = lfoAccumulator;\n                }\n                uint8_t lfoAccu8 = voiceIn->lfoAccumulator;\n                uint8_t lfoSample = 0;\n                \n                enum LFOWaveType lfoWaveType = voice->lfoAttr.wave;\n                switch (lfoWaveType)\n                {\n                    case LFOWaveTypeTriangle:\n                    {\n                        lfoSample = ((lfoAccu8 & 0x80) ? ~(lfoAccu8 << 1) : (lfoAccu8 << 1));\n                        break;\n                    }\n                    case LFOWaveTypeSawtooth:\n                    {\n                        lfoSample = ~lfoAccu8;\n                        break;\n                    }\n                    case LFOWaveTypeSquare:\n                    {\n                        lfoSample = (lfoAccu8 & 0x80) ? 0x00 : 0xFF;\n                        break;\n                    }\n                    case LFOWaveTypeRandom:\n                    {\n                        if ((lfoAccu8 & 0x80) != (lfoAccu8Last & 0x80))\n                        {\n                            uint16_t r = voiceIn->lfoRandom;\n                            uint16_t bit = ((r >> 0) ^ (r >> 2) ^ (r >> 3) ^ (r >> 5) ) & 1;\n                            voiceIn->lfoRandom = (r >> 1) | (bit << 15);\n                        }\n                        lfoSample = voiceIn->lfoRandom & 0xFF;\n                        break;\n                    }\n                }\n                \n                int freqAmount = lfoAmounts[voice->lfoOscAmount];\n                int volAmount = voice->lfoVolAmount;\n                int pwAmount = voice->lfoPWAmount;\n                \n                int freqMod = freq * lfoSample * freqAmount >> 16;\n                if (voice->lfoAttr.invert) freq -= freqMod; else freq += freqMod;\n                if (freq < 1) freq = 1;\n                if (freq > 65535) freq = 65535;\n                \n                if (voice->lfoAttr.invert)\n                {\n                    volume -= volume * lfoSample * volAmount >> 12;\n                }\n                else\n                {\n                    volume -= volume * (~lfoSample & 0xFF) * volAmount >> 12;\n                }\n                if (volume < 0) volume = 0;\n                if (volume > 255) volume = 255;\n                \n                int pwMod = lfoSample * pwAmount >> 4;\n                if (voice->lfoAttr.invert) pulseWidth -= pwMod; else pulseWidth += pwMod;\n                if (pulseWidth < 0) pulseWidth = 0;\n                if (pulseWidth > 254) pulseWidth = 254;\n                \n//                if (i == 0 && v == 0) printf(\"pulseWidth %d\\n\", pulseWidth);\n                \n                // --- WAVEFORM GENERATOR ---\n                \n                uint16_t accu16Last = ((uint32_t)voiceIn->accumulator >> 4) & 0xFFFF;\n                double accumulator = voiceIn->accumulator + (double)freq * 65536.0 / (double)outputFrequency;\n                if (accumulator >= overflow)\n                {\n                    // avoid overflow and loss of precision\n                    accumulator -= overflow;\n                }\n                voiceIn->accumulator = accumulator;\n                uint16_t accu16 = ((uint32_t)voiceIn->accumulator >> 4) & 0xFFFF;\n                \n                uint16_t sample = 0x7FFF; // silence\n                \n                enum WaveType waveType = voice->attr.wave;\n                switch (waveType)\n                {\n                    case WaveTypeSawtooth:\n                    {\n                        sample = accu16;\n                        break;\n                    }\n                    case WaveTypePulse:\n                    {\n                        sample = ((accu16 >> 8) > pulseWidth) ? 0xFFFF : 0x0000;\n                        break;\n                    }\n                    case WaveTypeTriangle:\n                    {\n                        sample = ((accu16 & 0x8000) ? ~(accu16 << 1) : (accu16 << 1));\n                        break;\n                    }\n                    case WaveTypeNoise:\n                    {\n                        if ((accu16 & 0x1000) != (accu16Last & 0x1000))\n                        {\n                            uint16_t r = voiceIn->noiseRandom;\n                            uint16_t bit = ((r >> 0) ^ (r >> 2) ^ (r >> 3) ^ (r >> 5) ) & 1;\n                            voiceIn->noiseRandom = (r >> 1) | (bit << 15);\n                        }\n                        sample = voiceIn->noiseRandom & 0xFFFF;\n                        break;\n                    }\n                }\n                \n                // --- TIMEOUT ---\n                \n                if (voice->attr.timeout)\n                {\n                    voiceIn->timeoutCounter -= 60.0 / outputFrequency;\n                    if (voiceIn->timeoutCounter <= 0.0)\n                    {\n                        voiceIn->timeoutCounter = 0.0;\n                        voice->status.gate = 0;\n                    }\n                }\n                \n                // --- ENVELOPE GENERATOR ---\n                \n                if (!voice->status.gate)\n                {\n                    voiceIn->envState = EnvStateRelease;\n                }\n                \n                switch (voiceIn->envState) {\n                    case EnvStateAttack:\n                        voiceIn->envCounter += envRates[voice->envA] / outputFrequency;\n                        if (voiceIn->envCounter >= 255.0)\n                        {\n                            voiceIn->envCounter = 255.0;\n                            voiceIn->envState = EnvStateDecay;\n                        }\n                        break;\n                        \n                    case EnvStateDecay:\n                        if (voiceIn->envCounter > voice->envS * 16.0)\n                        {\n                            voiceIn->envCounter -= envRates[voice->envD] / outputFrequency;\n                        }\n                        break;\n                        \n                    case EnvStateRelease:\n                        if (voiceIn->envCounter > 0.0)\n                        {\n                            voiceIn->envCounter -= envRates[voice->envR] / outputFrequency;\n                            if (voiceIn->envCounter < 0.0)\n                            {\n                                voiceIn->envCounter = 0.0;\n                            }\n                        }\n                        break;\n                }\n                \n                // --- OUTPUT ---\n                \n                volume = volume * (int)voiceIn->envCounter >> 8;\n                \n                // output peak to system registers\n                lifeRegisters->voices[v].peak = volume;\n                \n                int16_t voiceSample = (((int32_t)(sample - 0x7FFF)) * volume) >> 10; // 8 bit for volume, 2 bit for global\n                if (voice->status.mix & 0x01)\n                {\n                    leftOutput += voiceSample;\n                }\n                if (voice->status.mix & 0x02)\n                {\n                    rightOutput += voiceSample;\n                }\n            }\n            \n            // filter\n            \n            int32_t *filterBufferL = internals->filterBuffer[0];\n            int32_t *filterBufferR = internals->filterBuffer[1];\n            \n            for (int f = AUDIO_FILTER_BUFFER_SIZE - 1; f > 0; f--)\n            {\n                filterBufferL[f] = filterBufferL[f - 1];\n                filterBufferR[f] = filterBufferR[f - 1];\n            }\n            filterBufferL[0] = leftOutput;\n            filterBufferR[0] = rightOutput;\n            \n            leftOutput  = ((filterBufferL[0] >> 4) + (filterBufferL[1] >> 1) + (filterBufferL[2] >> 4));\n            rightOutput = ((filterBufferR[0] >> 4) + (filterBufferR[1] >> 1) + (filterBufferR[2] >> 4));\n        }\n        \n        stereoOutput[i++] = leftOutput >> volume;\n        stereoOutput[i++] = rightOutput >> volume;\n    }\n}\n"
  },
  {
    "path": "core/machine/audio_chip.h",
    "content": "//\n// Copyright 2016-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef audio_chip_h\n#define audio_chip_h\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#define NUM_VOICES 4\n#define NUM_AUDIO_BUFFERS 6\n#define AUDIO_FILTER_BUFFER_SIZE 3\n\n// audio output channels for stereo\n#define NUM_CHANNELS 2\n\nstruct Core;\n\nenum WaveType {\n    WaveTypeSawtooth,\n    WaveTypeTriangle,\n    WaveTypePulse,\n    WaveTypeNoise\n};\n\nenum EnvState {\n    EnvStateAttack,\n    EnvStateDecay,\n    EnvStateRelease\n};\n\nunion VoiceStatus {\n    struct {\n        uint8_t volume:4;\n        uint8_t mix:2;\n        uint8_t init:1;\n        uint8_t gate:1;\n    };\n    uint8_t value;\n};\n\nunion VoiceAttributes {\n    struct {\n        uint8_t pulseWidth:4;\n        uint8_t wave:2;\n        uint8_t timeout:1;\n    };\n    uint8_t value;\n};\n\nenum LFOWaveType {\n    LFOWaveTypeTriangle,\n    LFOWaveTypeSawtooth,\n    LFOWaveTypeSquare,\n    LFOWaveTypeRandom\n};\n\nunion LFOAttributes {\n    struct {\n        uint8_t wave:2;\n        uint8_t invert:1;\n        uint8_t envMode:1;\n        uint8_t trigger:1;\n    };\n    uint8_t value;\n};\n\nstruct Voice {\n    uint8_t frequencyLow;\n    uint8_t frequencyHigh;\n    union VoiceStatus status;\n    uint8_t peak;\n    union VoiceAttributes attr;\n    uint8_t length;\n    struct {\n        uint8_t envA:4;\n        uint8_t envD:4;\n    };\n    struct {\n        uint8_t envS:4;\n        uint8_t envR:4;\n    };\n    union LFOAttributes lfoAttr;\n    struct {\n        uint8_t lfoFrequency:4;\n        uint8_t lfoOscAmount:4;\n    };\n    struct {\n        uint8_t lfoVolAmount:4;\n        uint8_t lfoPWAmount:4;\n    };\n    uint8_t reserved2;\n};\n\nstruct AudioRegisters {\n    struct Voice voices[NUM_VOICES];\n};\n\nstruct VoiceInternals {\n    double accumulator;\n    uint16_t noiseRandom;\n    double envCounter;\n    enum EnvState envState;\n    double lfoAccumulator;\n    bool lfoHold;\n    uint16_t lfoRandom;\n    double timeoutCounter;\n};\n\nstruct AudioInternals {\n    struct VoiceInternals voices[NUM_VOICES];\n    struct AudioRegisters buffers[NUM_AUDIO_BUFFERS];\n    int readBufferIndex;\n    int writeBufferIndex;\n    bool audioEnabled;\n    int32_t filterBuffer[NUM_CHANNELS][AUDIO_FILTER_BUFFER_SIZE];\n};\n\nvoid audio_reset(struct Core *core);\nvoid audio_bufferRegisters(struct Core *core);\nvoid audio_renderAudio(struct Core *core, int16_t *output, int numSamples, int outputFrequency, int volume);\n\n#endif /* audio_chip_h */\n"
  },
  {
    "path": "core/machine/io_chip.h",
    "content": "//\n// Copyright 2016 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef io_chip_h\n#define io_chip_h\n\n#include <stdint.h>\n\n#define NUM_GAMEPADS 2\n\n// ================ Gamepad ================\n\nunion Gamepad {\n    struct {\n        uint8_t up:1;\n        uint8_t down:1;\n        uint8_t left:1;\n        uint8_t right:1;\n        uint8_t buttonA:1;\n        uint8_t buttonB:1;\n    };\n    uint8_t value;\n};\n\n// ================ Status ================\n\nunion IOStatus {\n    struct {\n        uint8_t pause:1;\n        uint8_t touch:1;\n    };\n    uint8_t value;\n};\n\n// ================ Attributes ================\n\nunion IOAttributes {\n    struct {\n        uint8_t gamepadsEnabled:2; // 0: off, 1...2: number of players\n        uint8_t keyboardEnabled:1;\n        uint8_t touchEnabled:1;\n    };\n    uint8_t value;\n};\n\n// ===============================================\n// ================ I/O Registers ================\n// ===============================================\n\nstruct IORegisters {\n    union Gamepad gamepads[NUM_GAMEPADS]; // 2 bytes\n    uint8_t touchX;\n    uint8_t touchY;\n    char key;\n    union IOStatus status;\n    union IOAttributes attr;\n};\n\n#endif /* io_chip_h */\n"
  },
  {
    "path": "core/machine/machine.c",
    "content": "//\n// Copyright 2016-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"machine.h\"\n#include <assert.h>\n#include <string.h>\n#include <stdbool.h>\n#include \"core.h\"\n\nvoid machine_init(struct Core *core)\n{\n    assert(sizeof(struct Machine) == 0x10000);\n}\n\nvoid machine_reset(struct Core *core, bool resetPersistent)\n{\n    // rom, video ram, working ram\n    memset(core->machine, 0, 0xE000);\n    \n    if (resetPersistent)\n    {\n        // persistent ram\n        memset(core->machine->persistentRam, 0, PERSISTENT_RAM_SIZE);\n    }\n    \n    // registers and reserved spaces\n    memset(core->machine->reservedMemory, 0, 0x1000);\n    \n    memset(core->machineInternals, 0, sizeof(struct MachineInternals));\n    audio_reset(core);\n}\n\nint machine_peek(struct Core *core, int address)\n{\n    if (address < 0 || address > 0xFFFF)\n    {\n        return -1;\n    }\n    if (address >= 0xE000 && address < 0xF000) // persistent\n    {\n        if (!core->machineInternals->hasAccessedPersistent)\n        {\n            delegate_persistentRamWillAccess(core, core->machine->persistentRam, PERSISTENT_RAM_SIZE);\n            core->machineInternals->hasAccessedPersistent = true;\n        }\n    }\n    \n    // read byte\n    return *(uint8_t *)((uint8_t *)core->machine + address);\n}\n\nbool machine_poke(struct Core *core, int address, int value)\n{\n    if (address < 0x8000 || address > 0xFFFF)\n    {\n        // cartridge ROM or outside RAM\n        return false;\n    }\n    if (address >= 0xF000 && address < 0xFE00)\n    {\n        // reserved memory\n        return false;\n    }\n    if (address >= 0xFF80)\n    {\n        // reserved registers\n        return false;\n    }\n    if (address == 0xFF76) // IO attributes\n    {\n        // check for illegal input change (gamepad <-> touch)\n        union IOAttributes currAttr = core->machine->ioRegisters.attr;\n        union IOAttributes newAttr;\n        newAttr.value = value;\n        if (currAttr.gamepadsEnabled > 0 && (newAttr.gamepadsEnabled == 0 || newAttr.touchEnabled))\n        {\n            return false;\n        }\n        if (currAttr.touchEnabled && (newAttr.touchEnabled == 0 || newAttr.gamepadsEnabled > 0))\n        {\n            return false;\n        }\n    }\n    else if (address >= 0xE000 && address < 0xF000) // persistent\n    {\n        if (!core->machineInternals->hasAccessedPersistent)\n        {\n            delegate_persistentRamWillAccess(core, core->machine->persistentRam, PERSISTENT_RAM_SIZE);\n            core->machineInternals->hasAccessedPersistent = true;\n        }\n        core->machineInternals->hasChangedPersistent = true;\n    }\n    \n    // write byte\n    *(uint8_t *)((uint8_t *)core->machine + address) = value & 0xFF;\n    \n    if (address == 0xFF76) // IO attributes\n    {\n        delegate_controlsDidChange(core);\n    }\n    else if (address >= 0xFF40 && address < 0xFF70) // audio\n    {\n        machine_enableAudio(core);\n    }\n    return true;\n}\n\nvoid machine_enableAudio(struct Core *core)\n{\n    if (!core->machineInternals->audioInternals.audioEnabled)\n    {\n        core->machineInternals->audioInternals.audioEnabled = true;\n        delegate_controlsDidChange(core);\n    }\n}\n\nvoid machine_suspendEnergySaving(struct Core *core, int numUpdates)\n{\n    if (core->machineInternals->energySavingTimer < numUpdates)\n    {\n        core->machineInternals->energySavingTimer = numUpdates;\n    }\n}\n"
  },
  {
    "path": "core/machine/machine.h",
    "content": "//\n// Copyright 2016 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef machine_h\n#define machine_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include \"io_chip.h\"\n#include \"video_chip.h\"\n#include \"audio_chip.h\"\n\n#define PERSISTENT_RAM_SIZE 4096\n\nstruct Core;\n\n// 64 KB\nstruct Machine {\n    \n    // 0x0000\n    uint8_t cartridgeRom[0x8000]; // 32 KB\n    \n    // 0x8000\n    struct VideoRam videoRam; // 8 KB\n    \n    // 0xA000\n    uint8_t workingRam[0x4000]; // 16 KB\n    \n    // 0xE000\n    uint8_t persistentRam[PERSISTENT_RAM_SIZE]; // 4 KB\n    \n    // 0xF000\n    uint8_t reservedMemory[0xFE00 - 0xF000];\n    \n    // 0xFE00\n    struct SpriteRegisters spriteRegisters; // 256 B\n    \n    // 0xFF00\n    struct ColorRegisters colorRegisters; // 32 B\n    \n    // 0xFF20\n    struct VideoRegisters videoRegisters;\n    uint8_t reservedVideo[0x20 - sizeof(struct VideoRegisters)];\n    \n    // 0xFF40\n    struct AudioRegisters audioRegisters;\n    \n    // 0xFF70\n    struct IORegisters ioRegisters;\n    uint8_t reservedIO[0x10 - sizeof(struct IORegisters)];\n    \n    // 0xFF80\n    uint8_t reservedRegisters[0x10000 - 0xFF80];\n};\n\nstruct MachineInternals {\n    struct AudioInternals audioInternals;\n    bool hasAccessedPersistent;\n    bool hasChangedPersistent;\n    bool isEnergySaving;\n    int energySavingTimer;\n};\n\nvoid machine_init(struct Core *core);\nvoid machine_reset(struct Core *core, bool resetPersistent);\nint machine_peek(struct Core *core, int address);\nbool machine_poke(struct Core *core, int address, int value);\nvoid machine_enableAudio(struct Core *core);\nvoid machine_suspendEnergySaving(struct Core *core, int numUpdates);\n\n#endif /* machine_h */\n"
  },
  {
    "path": "core/machine/video_chip.c",
    "content": "//\n// Copyright 2016-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"video_chip.h\"\n#include \"core.h\"\n#include <string.h>\n\n#define OVERLAY_FLAG (1<<6)\n\nint video_getCharacterPixel(struct Character *character, int x, int y)\n{\n    int b0 = (character->data[y] >> (7 - x)) & 0x01;\n    int b1 = (character->data[y | 8] >> (7 - x)) & 0x01;\n    return b0 | (b1 << 1);\n}\n\nvoid video_renderPlane(struct Character *characters, struct Plane *plane, int sizeMode, int y, int scrollX, int scrollY, int pixelFlag, uint8_t *scanlineBuffer)\n{\n    int divShift = sizeMode ? 4 : 3;\n    int planeY = y + scrollY;\n    int row = (planeY >> divShift) & 31;\n    int cellY = planeY & 7;\n    \n    int x = 0;\n    int b = 0;\n    uint8_t d0 = 0;\n    uint8_t d1 = 0;\n    uint8_t pal = 0;\n    uint8_t pri = 0;\n    \n    int pre = scrollX & 7;\n    \n    while (x < SCREEN_WIDTH)\n    {\n        if (!b)\n        {\n            int planeX = x + scrollX;\n            int column = (planeX >> divShift) & 31;\n            struct Cell *cell = &plane->cells[row][column];\n            \n            int index = cell->character;\n            if (sizeMode)\n            {\n                index += (cell->attr.flipX ? (planeX >> 3) + 1 : planeX >> 3) & 1;\n                index += ((cell->attr.flipY ? (planeY >> 3) + 1 : planeY >> 3) & 1) << 4;\n            }\n            \n            struct Character *character = &characters[index];\n            pal = cell->attr.palette << 2;\n            pri = cell->attr.priority << 7;\n            \n            int fcy = cell->attr.flipY ? (7 - cellY) : cellY;\n            d0 = character->data[fcy];\n            d1 = character->data[fcy | 8];\n            if (cell->attr.flipX)\n            {\n                // reverse bits hack from http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits\n                d0 = ((d0 * 0x0802LU & 0x22110LU) | (d0 * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;\n                d1 = ((d1 * 0x0802LU & 0x22110LU) | (d1 * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;\n            }\n        }\n        \n        if (pre)\n        {\n            --pre;\n        }\n        else\n        {\n            if (pri >= (*scanlineBuffer >> 7))\n            {\n                int pixel = ((d0 >> 7) & 1) | ((d1 >> 6) & 2);\n                if (pixel)\n                {\n                    *scanlineBuffer = pixel | pal | pri | pixelFlag;\n                }\n            }\n            ++scanlineBuffer;\n            ++x;\n        }\n\n        d0 <<= 1;\n        d1 <<= 1;\n        b = (b + 1) & 7;\n    }\n}\n\nvoid video_renderSprites(struct SpriteRegisters *reg, struct VideoRam *ram, int y, uint8_t *scanlineBuffer, uint8_t *scanlineSpriteBuffer)\n{\n    for (int i = NUM_SPRITES - 1; i >= 0; i--)\n    {\n        struct Sprite *sprite = &reg->sprites[i];\n        if (sprite->x != 0 || sprite->y != 0)\n        {\n            int spriteY = y - sprite->y + SPRITE_OFFSET_Y;\n            int size = (sprite->attr.size + 1) << 3;\n            if (spriteY >= 0 && spriteY < size)\n            {\n                if (sprite->attr.flipY)\n                {\n                    spriteY = size - spriteY - 1;\n                }\n                int charIndex = sprite->character + ((spriteY >> 3) << 4);\n                if (sprite->attr.flipX)\n                {\n                    charIndex += sprite->attr.size;\n                }\n                int minX = sprite->x - SPRITE_OFFSET_X;\n                int maxX = minX + size;\n                if (minX < 0)\n                {\n                    int skip = -minX >> 3;\n                    if (sprite->attr.flipX)\n                    {\n                        charIndex -= skip;\n                    }\n                    else\n                    {\n                        charIndex += skip;\n                    }\n                }\n                if (minX < 0) minX = 0;\n                if (maxX > SCREEN_WIDTH) maxX = SCREEN_WIDTH;\n                uint8_t *buffer = &scanlineSpriteBuffer[minX];\n                int spriteX = minX - sprite->x + SPRITE_OFFSET_X;\n                if (sprite->attr.flipX)\n                {\n                    spriteX = size - spriteX - 1;\n                }\n                struct Character *character = &ram->characters[charIndex];\n                for (int x = minX; x < maxX; x++)\n                {\n                    int pixel = video_getCharacterPixel(character, spriteX & 0x07, spriteY & 0x07);\n                    if (pixel)\n                    {\n                        *buffer = pixel | (sprite->attr.palette << 2) | (sprite->attr.priority << 7);\n                    }\n                    buffer++;\n                    if (sprite->attr.flipX)\n                    {\n                        if (!(spriteX & 0x07))\n                        {\n                            character--;\n                        }\n                        spriteX--;\n                    }\n                    else\n                    {\n                        spriteX++;\n                        if (!(spriteX & 0x07))\n                        {\n                            character++;\n                        }\n                    }\n                }\n            }\n        }\n    }\n    for (int x = 0; x < SCREEN_WIDTH; x++)\n    {\n        int pixel = *scanlineSpriteBuffer;\n        if (pixel && (pixel >> 7) >= (*scanlineBuffer >> 7))\n        {\n            *scanlineBuffer = pixel;\n        }\n        scanlineSpriteBuffer++;\n        scanlineBuffer++;\n    }\n}\n\nvoid video_renderScreen(struct Core *core, uint32_t *outputRGB)\n{\n    uint8_t scanlineBuffer[SCREEN_WIDTH];\n    uint8_t scanlineSpriteBuffer[SCREEN_WIDTH];\n    uint32_t *outputPixel = outputRGB;\n    \n    struct VideoRam *ram = &core->machine->videoRam;\n    struct VideoRegisters *reg = &core->machine->videoRegisters;\n    struct SpriteRegisters *sreg = &core->machine->spriteRegisters;\n    struct ColorRegisters *creg = &core->machine->colorRegisters;\n    for (int y = 0; y < SCREEN_HEIGHT; y++)\n    {\n        reg->rasterLine = y;\n        itp_runInterrupt(core, InterruptTypeRaster);\n        memset(scanlineBuffer, 0, sizeof(scanlineBuffer));\n        \n        bool skip = (core->interpreter->interruptOverCycles > 0);\n        if (!skip)\n        {\n            if (reg->attr.planeBEnabled)\n            {\n                int scrollX = reg->scrollBX | (reg->scrollMSB.bX << 8);\n                int scrollY = reg->scrollBY | (reg->scrollMSB.bY << 8);\n                video_renderPlane(ram->characters, &ram->planeB, reg->attr.planeBCellSize, y, scrollX, scrollY, 0, scanlineBuffer);\n            }\n            if (reg->attr.planeAEnabled)\n            {\n                int scrollX = reg->scrollAX | (reg->scrollMSB.aX << 8);\n                int scrollY = reg->scrollAY | (reg->scrollMSB.aY << 8);\n                video_renderPlane(ram->characters, &ram->planeA, reg->attr.planeACellSize, y, scrollX, scrollY, 0, scanlineBuffer);\n            }\n            if (reg->attr.spritesEnabled)\n            {\n                memset(scanlineSpriteBuffer, 0, sizeof(scanlineSpriteBuffer));\n                video_renderSprites(sreg, ram, y, scanlineBuffer, scanlineSpriteBuffer);\n            }\n        }\n        \n        // overlay\n        video_renderPlane((struct Character *)overlayCharacters, &core->overlay->plane, 0, y, 0, 0, OVERLAY_FLAG, scanlineBuffer);\n        \n        for (int x = 0; x < SCREEN_WIDTH; x++)\n        {\n            int colorIndex = scanlineBuffer[x] & 0x1F;\n            int color = (scanlineBuffer[x] & OVERLAY_FLAG) ? overlayColors[colorIndex] : skip ? 0 : creg->colors[colorIndex];\n            int r = (color >> 4) & 0x03;\n            int g = (color >> 2) & 0x03;\n            int b = color & 0x03;\n            \n            // add some gray (0x000F0F0F) to simulate screen\n#if __LIBRETRO__\n            *outputPixel = (b * 0x55) | ((g * 0x55) << 8) | ((r * 0x55) << 16) | 0x000F0F0F;\n#else\n            *outputPixel = (r * 0x55) | ((g * 0x55) << 8) | ((b * 0x55) << 16) | 0x000F0F0F;\n#endif\n\n            ++outputPixel;\n        }\n    }\n}\n"
  },
  {
    "path": "core/machine/video_chip.h",
    "content": "//\n// Copyright 2016 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef video_chip_h\n#define video_chip_h\n\n#include <stdio.h>\n#include <stdint.h>\n\n#define SCREEN_WIDTH 160\n#define SCREEN_HEIGHT 128\n#define NUM_CHARACTERS 256\n#define NUM_PALETTES 8\n#define PLANE_COLUMNS 32\n#define PLANE_ROWS 32\n#define NUM_SPRITES 64\n#define SPRITE_OFFSET_X 32\n#define SPRITE_OFFSET_Y 32\n\nstruct Core;\n\n// ================ Character ================\n\n// 16 bytes\nstruct Character {\n    uint8_t data[16];\n};\n\n// ================ Sprite ================\n\nunion CharacterAttributes {\n    struct {\n        uint8_t palette:3;\n        uint8_t flipX:1;\n        uint8_t flipY:1;\n        uint8_t priority:1;\n        uint8_t size:2; // 1x1 - 4x4 characters\n    };\n    uint8_t value;\n};\n\n// 4 bytes\nstruct Sprite {\n    uint8_t x;\n    uint8_t y;\n    uint8_t character;\n    union CharacterAttributes attr;\n};\n\n// ================ Cell ================\n\n// 2 bytes\nstruct Cell {\n    uint8_t character;\n    union CharacterAttributes attr;\n};\n\n// ================ Plane ================\n\n// 2048 bytes\nstruct Plane {\n    struct Cell cells[PLANE_ROWS][PLANE_COLUMNS];\n};\n\n// ===========================================\n// ================ Video RAM ================\n// ===========================================\n\n// 8 KB\nstruct VideoRam {\n    struct Character characters[NUM_CHARACTERS]; // 4 KB\n    struct Plane planeA; // 2 KB\n    struct Plane planeB; // 2 KB\n};\n\n// =================================================\n// ================ Video Registers ================\n// =================================================\n\nstruct SpriteRegisters {\n    struct Sprite sprites[NUM_SPRITES]; // 256 bytes\n};\n\nstruct ColorRegisters {\n    uint8_t colors[NUM_PALETTES * 4]; // 32 bytes\n};\n\nunion DisplayAttributes {\n    struct {\n        uint8_t spritesEnabled:1;\n        uint8_t planeAEnabled:1;\n        uint8_t planeBEnabled:1;\n        uint8_t planeACellSize:1;\n        uint8_t planeBCellSize:1;\n    };\n    uint8_t value;\n};\n\nunion ScrollMSB {\n    struct {\n        uint8_t aX:1;\n        uint8_t aY:1;\n        uint8_t bX:1;\n        uint8_t bY:1;\n    };\n    uint8_t value;\n};\n\nstruct VideoRegisters {\n    union DisplayAttributes attr;\n    uint8_t scrollAX;\n    uint8_t scrollAY;\n    uint8_t scrollBX;\n    uint8_t scrollBY;\n    union ScrollMSB scrollMSB;\n    uint8_t rasterLine;\n};\n\n// ===========================================\n// ================ Functions ================\n// ===========================================\n\nvoid video_renderScreen(struct Core *core, uint32_t *outputRGB);\n\n#endif /* video_chip_h */\n"
  },
  {
    "path": "core/overlay/overlay.c",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"overlay.h\"\n#include \"core.h\"\n#include \"io_chip.h\"\n#include <math.h>\n\nvoid overlay_clear(struct Core *core);\n\n\nvoid overlay_init(struct Core *core)\n{\n    struct TextLib *lib = &core->overlay->textLib;\n    lib->core = core;\n    lib->bg = OVERLAY_BG;\n    lib->windowBg = OVERLAY_BG;\n    lib->charAttr.priority = 1;\n    lib->charAttr.palette = 1;\n    lib->fontCharOffset = 0;\n    lib->windowX = 0;\n    lib->windowY = 0;\n    lib->windowWidth = 20;\n    lib->windowHeight = 16;\n    lib->cursorX = 0;\n    lib->cursorY = 0;\n}\n\nvoid overlay_reset(struct Core *core)\n{\n    overlay_clear(core);\n    core->overlay->textLib.cursorX = 0;\n    core->overlay->textLib.cursorY = 0;\n}\n\nvoid overlay_updateState(struct Core *core)\n{\n    overlay_clear(core);\n    \n    if (core->interpreter->state == StatePaused)\n    {\n        core->overlay->timer = 0;\n    }\n    \n    if (!core->interpreter->debug)\n    {\n        core->overlay->textLib.cursorX = 0;\n        core->overlay->textLib.cursorY = 0;\n    }\n}\n\nvoid overlay_message(struct Core *core, const char *message)\n{\n    struct TextLib *lib = &core->overlay->textLib;\n    txtlib_setCells(lib, 0, 15, 19, 15, 0);\n    txtlib_writeText(lib, message, 0, 15);\n    core->overlay->messageTimer = 120;\n    machine_suspendEnergySaving(core, 120);\n}\n\nvoid overlay_draw(struct Core *core, bool ingame)\n{\n    struct TextLib *lib = &core->overlay->textLib;\n    \n    if (core->overlay->messageTimer > 0)\n    {\n        core->overlay->messageTimer--;\n        if (core->overlay->messageTimer < 20)\n        {\n            txtlib_scrollBackground(lib, 0, 15, 19, 15, -1, 0);\n            txtlib_setCell(lib, 19, 15, 0);\n        }\n    }\n    \n    if (ingame)\n    {\n        if (core->interpreter->state == StatePaused)\n        {\n            if (core->overlay->timer % 60 < 40)\n            {\n                txtlib_writeText(lib, \"PAUSED\", 7, 7);\n            }\n            else\n            {\n                txtlib_setCells(lib, 7, 7, 12, 7, 0);\n            }\n        }\n        \n        if (core->interpreter->debug)\n        {\n            txtlib_writeText(lib, \"CPU\", 17, 0);\n            int cpuLoad = core->interpreter->cpuLoadDisplay;\n            if (cpuLoad < 100)\n            {\n                txtlib_writeNumber(lib, cpuLoad, 2, 17, 1);\n                txtlib_writeText(lib, \"%\", 19, 1);\n            }\n            else\n            {\n                txtlib_writeText(lib, \"MAX\", 17, 1);\n            }\n        }\n    }\n    \n    core->overlay->timer++;\n}\n\nvoid overlay_clear(struct Core *core)\n{\n    struct Plane *plane = &core->overlay->plane;\n    for (int y = 0; y < PLANE_ROWS; y++)\n    {\n        for (int x = 0; x < PLANE_COLUMNS; x++)\n        {\n            struct Cell *cell = &plane->cells[y][x];\n            cell->character = 0;\n            cell->attr.palette = 0;\n            cell->attr.priority = 1;\n        }\n    }\n    core->overlay->messageTimer = 0;\n}\n\n"
  },
  {
    "path": "core/overlay/overlay.h",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef overlay_h\n#define overlay_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"video_chip.h\"\n#include \"overlay_data.h\"\n#include \"text_lib.h\"\n\nstruct Core;\n\nstruct Overlay {\n    struct Plane plane;\n    struct TextLib textLib;\n    int timer;\n    int messageTimer;\n};\n\nvoid overlay_init(struct Core *core);\nvoid overlay_reset(struct Core *core);\nvoid overlay_updateState(struct Core *core);\nvoid overlay_message(struct Core *core, const char *message);\nvoid overlay_draw(struct Core *core, bool ingame);\n\n#endif /* overlay_h */\n"
  },
  {
    "path": "core/overlay/overlay_data.c",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"overlay_data.h\"\n\nuint8_t overlayColors[] = {\n    // gamepads\n    0,\n    (3 << 4) | (3 << 2) | 3,\n    (2 << 4) | (2 << 2) | 2,\n    (1 << 4) | (1 << 2) | 1,\n    // paused text\n    0,\n    (3 << 4) | (3 << 2) | 3,\n    0,\n    0\n};\n\nuint8_t overlayCharacters[] = {\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x24, 0x24, 0x24, 0x24, 0x3C, 0x24, 0x3C,\n    0xFE, 0xFE, 0xFE, 0xFE, 0x7E, 0x00, 0x00, 0x00, 0xFE, 0x92, 0x92, 0xDA, 0x7E, 0x00, 0x00, 0x00,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xDB, 0x81, 0xDB, 0xDB, 0x81, 0xDB, 0x7E,\n    0x1C, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x1C, 0x1C, 0x77, 0x41, 0x47, 0x71, 0x41, 0x77, 0x1C,\n    0xF7, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xEF, 0xF7, 0x9D, 0x9B, 0xF6, 0x6F, 0xD9, 0xB9, 0xEF,\n    0x3E, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3E, 0x62, 0x4A, 0xC7, 0x91, 0x9B, 0xC5, 0x7F,\n    0x3C, 0x3C, 0x7C, 0x7C, 0x78, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x64, 0x4C, 0x78, 0x00, 0x00, 0x00,\n    0x1E, 0x3E, 0x7E, 0x7C, 0x7C, 0x7E, 0x3E, 0x1E, 0x1E, 0x32, 0x66, 0x4C, 0x4C, 0x66, 0x32, 0x1E,\n    0x78, 0x7C, 0x7E, 0x3E, 0x3E, 0x7E, 0x7C, 0x78, 0x78, 0x4C, 0x66, 0x32, 0x32, 0x66, 0x4C, 0x78,\n    0x00, 0x7E, 0x7E, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0x00, 0x7E, 0x5A, 0xE7, 0x81, 0xE7, 0x5A, 0x7E,\n    0x00, 0x3C, 0x3C, 0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x00, 0x3C, 0x24, 0xE7, 0x81, 0xE7, 0x24, 0x3C,\n    0x00, 0x00, 0x00, 0x3C, 0x3C, 0x7C, 0x7C, 0x78, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x64, 0x4C, 0x78,\n    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x81, 0xFF, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x24, 0x3C,\n    0x0F, 0x1F, 0x3F, 0x7E, 0xFC, 0xF8, 0xF0, 0xE0, 0x0F, 0x19, 0x33, 0x66, 0xCC, 0x98, 0xB0, 0xE0,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0x91, 0x89, 0x99, 0xC3, 0x7E,\n    0x3C, 0x7C, 0x7C, 0x7C, 0x3C, 0xFF, 0xFF, 0xFF, 0x3C, 0x64, 0x44, 0x64, 0x24, 0xE7, 0x81, 0xFF,\n    0x7E, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0x7E, 0xC3, 0x99, 0xF3, 0x66, 0xCF, 0x81, 0xFF,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0xF3, 0xF9, 0x99, 0xC3, 0x7E,\n    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0xFF, 0x99, 0x99, 0x81, 0xF9, 0x09, 0x09, 0x0F,\n    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x81, 0x9F, 0x83, 0xF9, 0xF9, 0x83, 0xFE,\n    0x3E, 0x7E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3E, 0x62, 0xCE, 0x83, 0x99, 0x99, 0xC3, 0x7E,\n    0xFF, 0xFF, 0xFF, 0x3F, 0x7E, 0x7C, 0x78, 0x78, 0xFF, 0x81, 0xF9, 0x33, 0x66, 0x4C, 0x48, 0x78,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0xC3, 0x99, 0x99, 0xC3, 0x7E,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0xC1, 0xF9, 0x99, 0xC3, 0x7E,\n    0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x24, 0x3C, 0x24, 0x3C, 0x00,\n    0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x7C, 0x7C, 0x78, 0x00, 0x00, 0x3C, 0x24, 0x3C, 0x64, 0x4C, 0x78,\n    0x00, 0x1E, 0x3E, 0x7E, 0x7C, 0x7E, 0x3E, 0x1E, 0x00, 0x1E, 0x32, 0x66, 0x4C, 0x66, 0x32, 0x1E,\n    0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x81, 0xFF, 0x81, 0xFF, 0x00,\n    0x00, 0x78, 0x7C, 0x7E, 0x3E, 0x7E, 0x7C, 0x78, 0x00, 0x78, 0x4C, 0x66, 0x32, 0x66, 0x4C, 0x78,\n    0x7E, 0xFF, 0xFF, 0xFF, 0x3E, 0x3C, 0x3C, 0x3C, 0x7E, 0xC3, 0x99, 0xF3, 0x26, 0x3C, 0x24, 0x3C,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7E, 0x7E, 0xC3, 0x99, 0x91, 0x91, 0x9F, 0xC2, 0x7E,\n    0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x66, 0xC3, 0x99, 0x81, 0x99, 0x99, 0xFF,\n    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0x83, 0x99, 0x83, 0x99, 0x99, 0x83, 0xFE,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0x9F, 0x9F, 0x99, 0xC3, 0x7E,\n    0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xFC, 0x86, 0x93, 0x99, 0x99, 0x93, 0x86, 0xFC,\n    0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x9F, 0x84, 0x9C, 0x9F, 0x81, 0xFF,\n    0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xF0, 0xF0, 0xF0, 0xFF, 0x81, 0x9F, 0x84, 0x9C, 0x90, 0x90, 0xF0,\n    0x7E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC2, 0x9F, 0x91, 0x99, 0x99, 0xC3, 0x7E,\n    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF,\n    0x7E, 0x7E, 0x7E, 0x3C, 0x3C, 0x7E, 0x7E, 0x7E, 0x7E, 0x42, 0x66, 0x24, 0x24, 0x66, 0x42, 0x7E,\n    0x3F, 0x3F, 0x3F, 0x0F, 0xFF, 0xFF, 0xFF, 0x7E, 0x3F, 0x21, 0x39, 0x09, 0xF9, 0x99, 0xC3, 0x7E,\n    0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x93, 0x86, 0x86, 0x93, 0x99, 0xFF,\n    0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x9F, 0x81, 0xFF,\n    0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xBD, 0x99, 0x81, 0x81, 0x99, 0x99, 0xFF,\n    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0x89, 0x81, 0x91, 0x99, 0x99, 0xFF,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0x99, 0x99, 0x99, 0xC3, 0x7E,\n    0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xF0, 0xF0, 0xF0, 0xFE, 0x83, 0x99, 0x83, 0x9E, 0x90, 0x90, 0xF0,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7E, 0xC3, 0x99, 0x99, 0x95, 0x93, 0xC1, 0x7F,\n    0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFE, 0x83, 0x99, 0x83, 0x86, 0x93, 0x99, 0xFF,\n    0x7F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFE, 0x7F, 0xC1, 0x9F, 0xC3, 0x79, 0xF9, 0x83, 0xFE,\n    0xFF, 0xFF, 0xFF, 0x3C, 0x3C, 0x3C, 0x3C, 0x3C, 0xFF, 0x81, 0xE7, 0x24, 0x24, 0x24, 0x24, 0x3C,\n    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0x7E,\n    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0x66, 0x3C,\n    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0x99, 0x99, 0x81, 0x81, 0x99, 0xBD, 0xE7,\n    0xFF, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x99, 0xC3, 0x66, 0xC3, 0x99, 0x99, 0xFF,\n    0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x3C, 0x3C, 0xFF, 0x99, 0x99, 0xC3, 0x66, 0x24, 0x24, 0x3C,\n    0xFF, 0xFF, 0xFF, 0x7E, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xF3, 0x66, 0xCC, 0x9F, 0x81, 0xFF,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0x99, 0xA5, 0xBD, 0xA5, 0xC3, 0x7E,\n    0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x7E, 0xC3, 0xA1, 0xB9, 0xA5, 0xB9, 0xC3, 0x7E,\n    0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x3C, 0x66, 0xE7, 0x81, 0x81, 0xE7, 0x66, 0x3C,\n    0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x3C, 0x66, 0xC3, 0x99, 0xFF, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x81, 0xFF,\n};\n"
  },
  {
    "path": "core/overlay/overlay_data.h",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef overlay_data_h\n#define overlay_data_h\n\n#include <stdio.h>\n#include <stdint.h>\n#include \"video_chip.h\"\n\nextern uint8_t overlayColors[];\nextern uint8_t overlayCharacters[];\n\n#endif /* overlay_data_h */\n"
  },
  {
    "path": "docs/license.txt",
    "content": "Copyright 2016-2021 Timo Kloss\n\nThis software is provided 'as-is', without any express or implied\nwarranty. In no event will the authors be held liable for any damages\narising from the use of this software.\n\nPermission is granted to anyone to use this software for any purpose,\nincluding commercial applications, and to alter it and redistribute it\nfreely, subject to the following restrictions:\n\n1. The origin of this software must not be misrepresented; you must not\n   claim that you wrote the original software. If you use this software\n   in a product, an acknowledgment in the product documentation would be\n   appreciated but is not required.\n2. Altered source versions must be plainly marked as such, and must not be\n   misrepresented as being the original software.\n3. This notice may not be removed or altered from any source distribution.\n"
  },
  {
    "path": "docs/manual.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\" />\n<title>LowRes NX Manual</title>\n<style>\nbody {\n\tfont-family: sans-serif;\n}\ntd, th {\n    padding-right: 1em;\n    vertical-align: top;\n}\nth {\n    text-align: left;\n}\na {\n    text-decoration: none;\n    color: #055;\n}\nul {\n    padding-left: 25px;\n}\nli {\n    margin-bottom: 0.5em;\n}\nh1 {\n\tborder-top: 6px solid black;\n\tpadding-top: 6px;\n\ttext-transform: uppercase;\n\tclear: both;\n}\nh2 {\n\tborder-top: 2px solid black;\n\tpadding: 4px 0;\n\tclear: both;\n}\np + h1, ul + h1, pre + h1, table + h1, div + h1 {\n\tmargin-top: 3em;\n}\np + h2, ul + h2, pre + h2, table + h2, div + h2 {\n\tmargin-top: 2em;\n}\np + h3, ul + h3, pre + h3, table + h3, div + h3 {\n\tmargin-top: 1.5em;\n}\n.container {\n\tmargin-left: auto;\n\tmargin-right: auto;\n\tmax-width: 640px;\n\tpadding-left: 15px;\n\tpadding-right: 15px;\n}\n.info-mobile, .info-desktop {\n\tpadding-left: 38px;\n\tmargin: 1em 0;\n\tmin-height: 30px;\n\tbackground-repeat: no-repeat;\n\tbackground-position: top left;\n}\n.info-mobile {\n\tbackground-image: url(manual-images/icon_mobile.png);\n\tbackground-image: -webkit-image-set(url(manual-images/icon_mobile.png) 1x, url(manual-images/icon_mobile@2x.png) 2x);\n}\n.info-desktop {\n\tbackground-image: url(manual-images/icon_desktop.png);\n\tbackground-image: -webkit-image-set(url(manual-images/icon_desktop.png) 1x, url(manual-images/icon_desktop@2x.png) 2x);\n}\n.illustration {\n\tfloat: right;\n}\n.illustration-nofloat {\n\tdisplay: block;\n\tfloat: none;\n}\n@media (max-width: 480px) {\n\t.illustration, .illustration-nofloat {\n\t\tdisplay: block;\n\t\tfloat: none;\n\t\tmargin-left: auto;\n\t\tmargin-right: auto;\n\t}\n}\n@media (max-width: 400px) {\n\t.bigtable td {\n\t\tfont-size: 0.7em;\n\t}\n    .container {\n        padding-left: 8px;\n        padding-right: 8px;\n    }\n}\n.nobr {\n\twhite-space: nowrap;\n\thyphens: none;\n}\n.example {\n\tbackground-color: #eee;\n\tpadding: 4px 0px 4px 4px;\n}\n@media (max-width: 320px) {\n\t.example {\n\t\tfont-size: 0.9em;\n\t}\n}\n\n</style>\n</head>\n\n<body>\n<div class=\"container\">\n\n<h1 id=\"Introduction\">Introduction</h1>\n\n<img class=\"illustration\" src=\"manual-images/illustration_hello.png\" srcset=\"manual-images/illustration_hello.png 1x, manual-images/illustration_hello@2x.png 2x\"/>\n\n<h3 id=\"Program-retro-games-in-BASIC\">Program retro games in BASIC</h3>\n\n<p>Make your own retro games on a virtual game console. Program in the classic BASIC language and create sprites, tile maps, sound and music with the included tools. As a beginner you will quickly understand how to create simple text games or show your first sprite on a tile map. As an experienced programmer you can discover the full potential of retro hardware tricks!</p>\n\n<h3 id=\"Virtual-Game-Console\">Virtual Game Console</h3>\n\n<p>Imagine LowRes NX as a handheld game console with a d-pad, two action buttons and a little rubber keyboard below a slidable touchscreen. LowRes NX was inspired by real 8- and 16-bit systems and simulates chips for graphics, sound and I/O, which actually work like classic hardware. It supports hardware sprites as well as hardware parallax scrolling, and even offers vertical blank and raster interrupts to create authentic retro effects.</p>\n\n<h3 id=\"Old-School-Programming\">Old-School Programming</h3>\n\n<p>The programming language of LowRes NX is based on second-generation, structured BASIC. It offers all the classic commands, but with labels, loops and subprograms instead of line numbers. Graphics and sound are supported by additional commands and you can even access the virtual hardware directly using PEEK and POKE. You have complete control over the program flow, there is no standard update function to implement.</p>\n\n<h3 id=\"Creative-Tools\">Creative Tools</h3>\n\n<p>LowRes NX includes all the tools you need: The Gfx Designer for editing sprites, tiles, fonts and maps, as well as the Sound Composer for music and sound effects. All of these are just normal BASIC programs. You can change and improve them or even create your own custom editors.</p>\n\n<h3 id=\"Share-and-Play\">Share and Play</h3>\n\n<p>Send your games directly to other users or share them via the website. All programs are open source, so you can play them, learn from them and edit them. Do you prefer making just art or music? Share your creations as assets and let other programmers use them in their projects.</p>\n\n<img class=\"illustration-nofloat\" src=\"manual-images/illustration_open.png\" srcset=\"manual-images/illustration_open.png 1x, manual-images/illustration_open@2x.png 2x\"/>\n\n\n<h2 id=\"Specifications\">Specifications</h2>\n\n<ul>\n<li>Cartridge ROM: 32 KB for gfx, music, any binary data</li>\n<li>Code: BASIC, max 16384 tokens</li>\n<li>Screen: 160x128 pixels, 60 Hz</li>\n<li>Backgrounds: Two layers, tile-based, scrollable</li>\n<li>Sprites: 64, max 32x32 pixels</li>\n<li>Colors: 8 dynamic 6-bit palettes with 4 colors each</li>\n<li>Sound: 4 voices, saw/tri/pulse/noise, pulse width, volume, ADSR, LFO</li>\n<li>Input: Two game controllers with d-pad and two buttons + pause</li>\n<li>Optional input: Keyboard and touchscreen/mouse</li>\n</ul>\n\n\n<h1 id=\"Contents\">Contents</h1>\n\n<ul>\n\t<li><a href=\"#Getting-Started\">Getting Started</a></li>\n\t<li><a href=\"#Editing\">Editing</a></li>\n\t<li><a href=\"#Gfx-Designer\">Gfx Designer</a></li>\n\t<li><a href=\"#Sound-Composer\">Sound Composer</a></li>\n\t<li><a href=\"#Language-Basics\">Language Basics</a></li>\n\t<li><a href=\"#Program-Flow-Control\">Program Flow Control</a></li>\n\t<li><a href=\"#Text\">Text</a></li>\n\t<li><a href=\"#User-Input\">User Input</a></li>\n\t<li><a href=\"#Graphics\">Graphics</a></li>\n\t<li><a href=\"#Sound\">Sound</a></li>\n\t<li><a href=\"#Data\">Data</a></li>\n\t<li><a href=\"#Memory-Access\">Memory Access</a></li>\n\t<li><a href=\"#Files\">Files</a></li>\n\t<li><a href=\"#Math-Functions\">Math Functions</a></li>\n\t<li><a href=\"#String-Functions\">String Functions</a></li>\n\t<li><a href=\"#System\">System</a></li>\n\t<li><a href=\"#Advanced-Topics\">Advanced Topics</a></li>\n</ul>\n\n\n<h1 id=\"Getting-Started\">Getting Started</h1>\n\n<img class=\"illustration\" src=\"manual-images/illustration_playing.png\" srcset=\"manual-images/illustration_playing.png 1x, manual-images/illustration_playing@2x.png 2x\"/>\n\n<p>Try some of the included programs to see how LowRes NX\ncan look like. Have a look at the action game <i>LowRes Galaxy 2</i>,\nthe text adventure <i>LowRes Adventure</i>, and the demo <i>Star Scroller</i>.</p>\n\n<div class=\"info-mobile\">\nOn the \"My Programs\" screen select a program to open the source code\neditor. Then tap on the Play button to run it.\n</div>\n\n<div class=\"info-desktop\">\nFind the examples in the folder \"programs\".  Open the LowRes NX\napplication and drag and drop any .nx file into its window. You can\nalso select LowRes NX as default application for .nx files, so\nprograms can be started simply by double clicking them.\n</div>\n\n<p>Once you have played enough, you can create your first own program.</p>\n\n<div class=\"info-mobile\">\nOn the \"My Programs\" screen tap on the Plus button to create a new\nprogram. Select it to open the source code editor.\n</div>\n\n<div class=\"info-desktop\">\nUse any text editor to create a new file.\nOn Windows make sure the text editor supports Mac/Linux line ends,\notherwise you may see everything in one line.\n</div>\n\n<p>Type these lines:</p>\n\n<pre class=\"example\">\nPRINT \"WELCOME!\"\nPRINT \"WHAT IS YOUR NAME?\"\nINPUT \">\";N$\nPRINT \"HELLO \";N$;\"!\"\n</pre>\n\n<div class=\"info-desktop\">\nSave your program file with a useful name and the extension \".nx\".\n</div>\n\n<p>Now run your program. This is a little example using the keyboard.\nLet's try something with a gamepad. Create another new program and\ntype this:</p>\n\n<pre class=\"example\">\nGAMEPAD 1\nX=76\nY=60\nDO\n  IF UP(0) THEN Y=Y-1\n  IF DOWN(0) THEN Y=Y+1\n  IF LEFT(0) THEN X=X-1\n  IF RIGHT(0) THEN X=X+1\n  SPRITE 0,X,Y,225\n  WAIT VBL\nLOOP\n</pre>\n\n<p>Run this program and you will see an \"A\" on the screen which you can\nmove around using the gamepad.</p>\n\n<div class=\"info-mobile\">\nWith the program still running, tap the menu button on the top right\nand select \"Capture Program Icon\".\nNow exit the program and return to the \"My Programs\" screen. There\nyou will see your program with a new image. Long tap the icon and\nselect \"Rename...\" to give it a better name.\n</div>\n\n\n<h1 id=\"Editing\">Editing</h1>\n\n<h2 id=\"Programs-And-Data\">Programs and Data</h2>\n\n<img class=\"illustration\" src=\"manual-images/illustration_disk.png\" srcset=\"manual-images/illustration_disk.png 1x, manual-images/illustration_disk@2x.png 2x\"/>\n\n<p>A program file contains a complete game or application, including all\nits data, stored as simple text. The first part is the BASIC source code.\nPlease read the programming chapters for further explanation.</p>\n\n<p>The second part are the cartridge ROM entries. These are up to 16\nnumbered data blocks, which can contain any kind of data, for\nexample graphics, level maps, music, etc. When a program is running,\nall its ROM entries are accessible in the first 32 KB of the memory.</p>\n\n<p>You can easily create and edit ROM entries by using tools.\nTools are normal NX programs, but they are specifically\nmade for editing data. They can access any NX file as a \"virtual disk\"\nand use its ROM entries like files.</p>\n\n<p>There are two ways of using tools:</p>\n\n<ul>\n<li>You can open tools directly like other NX programs. They will use\nthe \"Disk.nx\" file in the tool's folder for loading and saving their\ndata.</li>\n<li>You can open any program you want to edit and select a tool from a\nmenu. This way the tool will access directly the data of the current\nprogram.</li>\n</ul>\n\n<p>Let's try it. Open your program with the moving \"A\" (from the\n\"Getting Started\" chapter) and select \"Gfx Designer\" from the Tools\nmenu.</p>\n\n<div class=\"info-mobile\">\nOpen your program, tap the Tools button and select the tool.\n</div>\n\n<div class=\"info-desktop\">\nRun your program and press the Escape key to enter the development\nmenu. Then press the Edit (ED) button and select the tool.\nBy default the tools menu is empty, so drag and drop the programs\n\"Gfx Designer\" and \"Sound Designer\" into the window.\n</div>\n\n<p>Draw something as character #1 (keep #0 empty), then tap on \"File\"\nand save as file 2 (\"Main Characters\"). Now return to the source code editor and you will see some hexadecimal data below your program.\nThis is your image! To see it, change the line</p>\n<pre class=\"example\">\nSPRITE 0,X,Y,225\n</pre>\n\n<p>to</p>\n\n<pre class=\"example\">\nSPRITE 0,X,Y,1\n</pre>\n\n<p>and run your program. There it is!</p>\n\n<div class=\"info-desktop\">\nPress Ctrl+R in the LowRes NX application to reload and run your\ncurrent program. The Run button in the developer menu does the same.\n</div>\n\n<p>Keep in mind that tools don't save automatically, so never forget\nto save before you exit them.</p>\n\n\n<h2 id=\"Standard-ROM-Entries\">Standard ROM Entries</h2>\n\n<p>For an easy start you should use the ROM entry numbers of the\nfollowing table. Their data is made ready for use automatically.\n</p>\n<table>\n<tr><td>#0</td><td>Keep empty for default font</td></tr>\n<tr><td>#1</td><td>Color palettes</td></tr>\n<tr><td>#2</td><td>Characters (sprites, tiles)</td></tr>\n<tr><td>#3</td><td>Background (tile map)</td></tr>\n<tr><td>#15</td><td>Sounds and music</td></tr>\n</table>\n\n<p>If cartridge ROM entry 0 is not used by a program, the compiler adds\ncharacter data for the default font. It occupies the characters\n192-255 and is automatically copied to video RAM on program start.\nIf you want to use the default font, make sure to keep ROM entry 0\nunused.</p>\n\n\n<h1 id=\"Gfx-Designer\">Gfx Designer</h1>\n\n<img class=\"illustration\" src=\"manual-images/illustration_painting.png\"  srcset=\"manual-images/illustration_painting.png 1x, manual-images/illustration_painting@2x.png 2x\"/>\n\n<p>Use the Gfx Designer to draw characters (sprites, tiles, fonts),\ndesign backgrounds (tile maps, screen layouts) and edit color palettes.</p>\n\n<p>On the bottom right there are three tabs for the different\nsections of the program: The character editor, the palette editor and\nthe background editor. You can use the keys 1, 2 and 3 to switch\nbetween them when running LowRes NX on a computer.</p>\n\n<p>The Gfx Designer loads the main palettes (file 1), characters (file 2),\nand background (file 3) on startup, but keep in mind that it doesn't\nsave automatically.</p>\n\n\n<h2 id=\"GD-Character-Editor\">Character Editor</h2>\n\n<p>At the bottom part you see the 256 characters split into four pages.\nThere you can select the current one for editing. Keep the last page\nempty, if you want to use the default font. Also character #0 should\nbe empty for a clean background.</p>\n<p>The top left square is for drawing the current character using the\nselected color.</p>\n\n<table>\n<tr><td>FILE</td><td>Go to the characters file menu.</td></tr>\n<tr><td>MOV</td><td>Move the pixels of the current character.</td></tr>\n<tr><td>16*16</td><td>Go to the 16x16-pixel edit screen.</td></tr>\n<tr><td>FLIP</td><td>Flip the current character horizontally or vertically.</td></tr>\n<tr><td>SPIN</td><td>Rotate the current character.</td></tr>\n<tr><td>CLR</td><td>Clear the current character with the selected\ncolor.</td></tr>\n<tr><td>CUT</td><td>Copy and clear the current character.</td></tr>\n<tr><td>COP</td><td>Copy the current character.</td></tr>\n<tr><td>PAS</td><td>Paste the copied character.</td></tr>\n<tr><td>SEL</td><td>Change the size of the selection.</td></tr>\n</table>\n\n<p>The size of the selection affects all edit tools on this screen, as\nwell as drawing in the background editor.</p>\n\n\n<h2 id=\"GD-Palette-Editor\">Palette Editor</h2>\n\n<p>The bottom part is identical to the character editor.</p>\n<p>In the box at the left you select the current color palette.</p>\n<p>On the right you can edit each color of the current\npalette using the RGB sliders. The first color is usually transparent\nand unused, except the first color of palette 0, which is used as the\nscreen backdrop color.</p>\n\n<table>\n<tr><td>FILE</td><td>Go to the palettes file menu.</td></tr>\n</table>\n\n\n<h2 id=\"GD-Background-Editor\">Background Editor</h2>\n\n<img class=\"illustration\" src=\"manual-images/illustration_building.png\" srcset=\"manual-images/illustration_building.png 1x, manual-images/illustration_building@2x.png 2x\"/>\n\n<p>Here you draw your background using the character and color palette\ncurrently selected on the other screens.</p>\n\n<table>\n<tr><td>FILE</td><td>Go to the background file menu.</td></tr>\n<tr><td>Pan</td><td>Drag the visible map area.</td></tr>\n<tr><td>Draw</td><td>Draw with the current mode.</td></tr>\n<tr><td>Erase</td><td>Erase cells including their attributes.</td></tr>\n<tr><td>FLIP</td><td>Toggles the X/Y flip attributes.</td></tr>\n<tr><td>PRI</td><td>Toggles the priority attribute.</td></tr>\n</table>\n\n<p>You can use the arrow or WASD keys to scroll the map when running LowRes\nNX on a computer. The keys Z, X, C, V and B select the tool.</p>\n\n<p>The draw tool has different modes. By tapping the button again,\nwhen it's already selected, you can change the mode:</p>\n\n<table>\n<tr><td>Draw</td><td>Edit single cells.</td></tr>\n<tr><td>Fill</td><td>Edit areas by dragging.</td></tr>\n<tr><td>Character</td><td>Draw with the selected character and color\npalette, using the selected flip and priority attributes.</td></tr>\n<tr><td>Palette</td><td>Change the color palette only.</td></tr>\n<tr><td>Priority</td><td>Change the priority only. Cells with priority\n1 are shown in green, all others in red.</td></tr>\n<tr><td>Stamp</td><td>Select an area by dragging and then draw with it\n(copy and paste).</td></tr>\n</table>\n\n\n<h2 id=\"GD-File-Menu\">File Menu</h2>\n\n<p>From here you load and save characters, color palettes and the\nbackground separately. There is no option to save everything at once.</p>\n\n<p>The list shows all 16 files of the current virtual disk, or in\nother words the ROM entries of the program you are editing. Select one\nand tap on Load or Save.</p>\n\n<h3 id=\"GD-File-Characters\">Characters</h3>\n<table>\n<tr><td>NEW</td><td>Clear all characters.\n</td></tr>\n<tr><td>FON</td><td>Copy a standard font to the current page.</td></tr>\n<tr><td>PAG</td><td>Load only one page from the selected file to the\ncurrent page. Useful to include fonts from another file, for example.</td></tr>\n</table>\n\n<h3 id=\"GD-File-Palettes\">Palettes</h3>\n<table>\n<tr><td>NEW</td><td>Reset to the default colors.\n</td></tr>\n</table>\n\n<h3 id=\"GD-File-Backgrounds\">Backgrounds</h3>\n<table>\n<tr><td>NEW</td><td>Clear the complete background (keeping its size).\n</td></tr>\n<tr><td>SIZE</td><td>Go to the size menu, where you can choose\nthe width and height of the background (in cells), as well as the cell\nsize (8x8 or 16x16 pixels).</td></tr>\n</table>\n\n\n<h1 id=\"Sound-Composer\">Sound Composer</h1>\n\n<img class=\"illustration\" src=\"manual-images/illustration_music.png\" srcset=\"manual-images/illustration_music.png 1x, manual-images/illustration_music@2x.png 2x\"/>\n\n<p>The Sound Composer serves to create sound presets, sequences\n(\"tracks\") and complete songs.</p>\n<p>It loads the main sound data (file 15) on startup, but keep in mind\nthat it doesn't save automatically. A sound file includes all sound\nand music data.</p>\n\n\n<h2 id=\"SC-Structure\">Structure</h2>\n\n<h3 id=\"SC-Sounds\">16 Sounds</h3>\n\n<p>A sound is a preset with all available sound parameters (waveform,\nenvelope, LFO, etc.). It can be used directly with the PLAY command in\nyour program or as an instrument in a track/song.</p>\n\n<h3 id=\"SC-Tracks\">64 Tracks</h3>\n\n<p>A track is a sequence of 32 steps for a single voice, where each\nstep can play a note or modify the sound. It can be used in your\nprogram for complex sound effects and short melodies (e.g. \"level up\")\nwith the TRACK command. Tracks are also used to create songs.</p>\n\n<h3 id=\"SC-Patterns\">64 Patterns</h3>\n\n<p>A pattern is a block of music, which defines which track should be\nplayed on each of the four voices. Patterns will be played one after\nanother, except for the following cases: If the next pattern is empty,\nthe song is stopped. When the current pattern finishes and has a \"loop\nend\" flag, the player jumps back to the previous pattern with a \"loop\nstart\" flag. When a pattern finishes and has a \"stop\" flag, the player\nstops.<br/>\nIf you want to create music for a game and you plan to use\nadditional sound effects, you should leave at least one voice empty.</p>\n\n<h3 id=\"SC-Songs\">Songs</h3>\n\n<p>The 64 patterns can be used for one long song or several shorter\nones. By using the \"loop\" and \"stop\" flags songs can be separated. To\nplay one specific song, just start at its first pattern. To play a\nsong in your program use the MUSIC command.</p>\n\n\n<h2 id=\"SC-Editors\">Editors</h2>\n\n<p>The Sound Composer has three tabs in the top right corner for the\ndifferent sections: The pattern editor, the track editor and the sound\neditor. This is a \"Tracker\"-style program, which means the timeline\ngoes from top to bottom, not from left to right. So the steps in a\ntrack are also called rows. Most values in the range from 0 to 15 are\nshown in hexadecimal format (0-F).</p>\n<p>There is some keyboard support when running LowRes NX on a\ncomputer: The arrow keys move the cursor and the key rows simulate a\nmusical keyboard with two octaves to enter notes. The return key\nenters a note stop, backspace deletes the current note and space\ntoggles between play (pattern/track) and stop.\n</p>\n\n<h3 id=\"SC-The-Pattern-Editor\">The Pattern Editor</h3>\n\n<p>Here you can select a pattern, choose its tracks for all voices and\nedit directly the notes of each selected track. There is also a toggle\nbutton for the \"loop\" and \"stop\" flags for the current pattern. If you\nset notes in a voice without track, it selects automatically a free\ntrack. Use the star symbol from the musical keyboard, if you want to\nstop/release a note in a track.</p>\n\n<h3 id=\"SC-The-Track-Editor\">The Track Editor</h3>\n\n<p>Here you can select and edit a single track with additional\nparameters:</p>\n<table>\n<tr><td>S</td><td>Sound</td></tr>\n<tr><td>V</td><td>Volume</td></tr>\n<tr><td>C</td><td>Sound command</td></tr>\n<tr><td>P</td><td>Parameter</td></tr>\n</table>\n\n\n<h3 id=\"SC-The-Sound-Editor\">The Sound Editor</h3>\n\n<p>Define and test your sounds here. The currently selected sound will\nbe used in the other editors for new notes.</p>\n\n\n<h2 id=\"SC-Sound-Commands\">Sound Commands</h2>\n\n<p>These commands allow you to change parameters dynamically while\nplayback. Use them in the track editor.</p>\n\n<table>\n<tr><th>C</th><th>P</th><th>Purpose</th></tr>\n<tr><td>0</td><td>0</td><td>No command</td></tr>\n<tr><td>0</td><td>x</td><td>Mix (1=left, 2=right, 3=center/both)</td></tr>\n<tr><td>1</td><td>x</td><td>Attack Time</td></tr>\n<tr><td>2</td><td>x</td><td>Decay Time</td></tr>\n<tr><td>3</td><td>x</td><td>Sustain Level</td></tr>\n<tr><td>4</td><td>x</td><td>Release Time</td></tr>\n<tr><td>5</td><td>x</td><td>LFO Rate</td></tr>\n<tr><td>6</td><td>x</td><td>LFO Frequency Amount</td></tr>\n<tr><td>7</td><td>x</td><td>LFO Volume Amount</td></tr>\n<tr><td>8</td><td>x</td><td>LFO Pulse Width Amount</td></tr>\n<tr><td>9</td><td>x</td><td>Pulse Width</td></tr>\n<tr><td>D</td><td>x</td><td>Slow Speed (like E, but +16)</td></tr>\n<tr><td>E</td><td>x</td><td>Speed (ticks per row, 8 by default)</td></tr>\n<tr><td>F</td><td>0</td><td>Break Track/Pattern</td></tr>\n<tr><td>F</td><td>1</td><td>Cut Note / Volume 0</td></tr>\n</table>\n\n\n<h1 id=\"Language-Basics\">Language Basics</h1>\n\n<p>The programming language of LowRes NX is based on\nsecond-generation, structured BASIC (1985 style).</p>\n\n<p>This manual is meant to be a reference for people\nwith prior programming experience. If you are\nnew to programming, you should look for tutorials on\nthe <a href=\"https://lowresnx.inutilis.com/help.php\" target=\"_blank\">LowRes NX website</a>\nand then check this manual for further details.</p>\n\n<h2 id=\"Types-and-Variables\">Types and Variables</h2>\n\n<p>Available value types are strings and numbers (floating point).</p>\n\n<p>Variable names can contain letters (A-Z), digits (0-9) and underscores\n(_), but cannot begin with a digit. Reserved keywords\n(commands, functions) cannot be used as variable names, but they can\nbe part of them. There is a list in the chapter <a href=\"#Reserved-Keywords\">\"Reserved Keywords\"</a>.</p>\n\n<p>Examples of valid variable names:</p>\n\n<pre class=\"example\">\nX\nA1\nLONG_VARIABLE_NAME\nPRINTER\n</pre>\n\n<p>Examples of invalid variable names:</p>\n\n<pre class=\"example\">\n2B\nPRINT\n</pre>\n\n<p>String variable names end with a $ symbol, for example:</p>\n\n<pre class=\"example\">\nA$\nNAME$\n</pre>\n\n<p>Variables are not explicitly declared, but they need to be\ninitialized with a value before you can read from them. Values are\nassigned to variables using the equal symbol:</p>\n\n<pre class=\"example\">\nNAME$=\"LOWRES NX\"\nLIVES=3\n</pre>\n\n<p>Hexadecimal and binary notation can be used for number values:</p>\n\n<pre class=\"example\">\n$FF02\n%11001011\n</pre>\n\n<h2 id=\"Arrays\">Arrays</h2>\n\n<h3 id=\"_DIM\" name=\"DIM\">DIM</h3>\n\n<pre>DIM [GLOBAL] var-list</pre>\n\n<p>Defines arrays with the highest index for each dimension:</p>\n\n<pre class=\"example\">\nDIM A(100)\nDIM MAP(31,23,1),NAMES$(9),SCORES(9)\n</pre>\n\n<p>Access elements from arrays, indices start from 0:</p>\n\n<pre class=\"example\">\nDIM SCORES(9)\nSCORES(0)=100\nSCORES(9)=5\nPRINT SCORES(0),SCORES(9)\n</pre>\n\n<p>All elements of arrays are automatically initialized, using zeros\n(0) or empty strings (\"\").</p>\n\n<p>With the optional GLOBAL keyword the arrays will be accessable\nfrom all subprograms.</p>\n\n\n<h3 id=\"_UBOUND\" name=\"UBOUND\">=UBOUND</h3>\n<pre>\nUBOUND(var[,dimension])\n</pre>\n\n<p>Returns the upper bound (the highest index) of a dimension of the\narray <em>var</em>.</p>\n\n<p>The <em>dimension</em> parameter is an optional number\nused when the array is multi-dimensional, and specifies the dimension\nof the array. The default value is 1.</p>\n\n<pre class=\"example\">\nDIM SCORES(9)\nFOR I=0 TO UBOUND(SCORES)\n  PRINT SCORES(I)\nNEXT I\n</pre>\n\n\n<h2 id=\"Labels\">Labels</h2>\n\n<p>A label marks a position in a program and is used for commands\nlike GOTO. It consists of a name, using the same rules as for\nvariables, followed by a colon.</p>\n\n<pre class=\"example\">\nTESTLABEL:\n</pre>\n\n<h2 id=\"Operators\">Operators</h2>\n\n<h3 id=\"Operators-Ari\">Arithmetic</h3>\n\n<table>\n<tr><th>Symbol</th> <th>Example</th> <th>Purpose</th></tr>\n<tr><td>-</td> <td><code>-B</code></td> <td>Negation</td></tr>\n<tr><td>^</td> <td><code>X^3</code></td> <td>Exponentiation</td></tr>\n<tr><td>*</td> <td><code>2*Y</code></td> <td>Multiplication</td></tr>\n<tr><td>/</td> <td><code>X/2</code></td> <td>Division</td></tr>\n<tr><td>\\</td> <td><code>X\\2</code></td> <td>Integer Division</td></tr>\n<tr><td>+</td> <td><code>C+2</code></td> <td>Addition</td></tr>\n<tr><td>-</td> <td><code>100-D</code></td> <td>Subtraction</td></tr>\n<tr><td>MOD</td> <td><code>X&#160;MOD&#160;2</code></td> <td>Modulo</td></tr>\n</table>\n\n<p>Operations are performed in mathematical order, for example\nmultiplications and divisions are performed before additions and\nsubtractions.</p>\n\n<pre class=\"example\">\nPRINT 10+2*5-8/4+5^2\n</pre>\n\n<p>The order can be specified explicitly through the use of\nparentheses, for example:</p>\n\n<pre class=\"example\">\nPRINT (3+4)*5\n</pre>\n\n<h3 id=\"Operators-Rel\">Relational</h3>\n\n<table>\n<tr><th>Symbol</th> <th>Example</th> <th>Purpose</th></tr>\n<tr><td>=</td> <td><code>A=10</code></td> <td>Equal</td></tr>\n<tr><td>&lt;&gt;</td> <td><code>A&lt;&gt;100</code></td> <td>Not equal</td></tr>\n<tr><td>&gt;</td> <td><code>B&gt;C</code></td> <td>Greater than</td></tr>\n<tr><td>&lt;</td> <td><code>5&lt;X</code></td> <td>Less than</td></tr>\n<tr><td>&gt;=</td> <td><code>X&gt;=20</code></td> <td>Greater than or equal</td></tr>\n<tr><td>&lt;=</td> <td><code>X&lt;=30</code></td> <td>Less than or equal</td></tr>\n</table>\n\n<p>A relational operator expression returns the value true (-1) or\nfalse (0).</p>\n\n<h3 id=\"Operators-Log\">Logical/Bitwise</h3>\n\n<table>\n<tr><th>Symbol</th> <th>Example</th> <th>Purpose</th></tr>\n<tr><td>NOT</td> <td><code>NOT&#160;(X=15)<br/>NOT&#160;0</code></td> <td>\"Not\"</td></tr>\n<tr><td>AND</td> <td><code>A=1&#160;AND&#160;B=12<br/>170&#160;AND&#160;15</code></td> <td>\"And\"</td></tr>\n<tr><td>OR</td> <td><code>X=10&#160;OR&#160;Y=0<br/>128&#160;OR&#160;2</code></td> <td>\"Or\"</td></tr>\n<tr><td>XOR</td> <td><code>A&#160;XOR&#160;B</code></td> <td>\"Exclusive Or\"</td></tr>\n</table>\n\n<h3 id=\"Operators-How\">How to Use Operators</h3>\n\n<p>All operators are available for numbers. Relational and addition\noperators are usable with strings, too:</p>\n\n<pre class=\"example\">\nSUM=1+3\nIF SUM&lt;5 THEN PRINT \"LESS THAN 5\"\nNAME$=\"LOWRES NX\"\nGREET$=\"HELLO \"+NAME$+\"!\"\nIF NAME$&gt;\"LOWRES\" THEN PRINT GREET$\nIF SUM=4 AND NAME$&lt;&gt;\"\" THEN PRINT \"OK\"\n</pre>\n\n\n<h1 id=\"Program-Flow-Control\">Program Flow Control</h1>\n\n<h2 id=\"Basics\">Basics</h2>\n\n<h3 id=\"_REM\" name=\"REM\">REM</h3>\n<pre>\nREM remark\n' remark\n</pre>\n\n<p>Allows you to put comments into your program. REM lines are not\nexecuted. You can use an apostrophe (') in place of the word REM.</p>\n\n<pre class=\"example\">\nREM THIS IS A TEST PROGRAM\nREM WRITTEN BY TIMO KLOSS\nPRINT \"HELLO\"\n'SHOW MORE TEXT...\nPRINT \"BYE\"\n</pre>\n\n\n<h3 id=\"_IF-THEN-ELSE\" name=\"IF,THEN,ELSE\">IF...THEN...ELSE</h3>\n<pre>\nIF expr THEN command [ELSE command]\n</pre>\n\n<p>Checks if the given expression is true or false. If it's true, the\ncommand after THEN is executed, otherwise the one after ELSE. The ELSE\npart is optional.</p>\n\n<pre class=\"example\">\nINPUT \"NUMBER A:\";A\nINPUT \"NUMBER B:\";B\nPRINT \"SAME? \";\nIF A=B THEN PRINT \"YES\" ELSE PRINT \"NO\"\nIF A&lt;B THEN PRINT \"A LESS THAN B\"\nIF A&gt;B THEN PRINT \"A GREATER THAN B\"\n</pre>\n\n<p>If you want to execute more than one command (or avoid long lines),\nyou can use the block version of the IF command. It must be closed\nwith the line END IF.</p>\n\n<pre>\nIF expression THEN\n  commands\n[ELSE IF expression THEN]\n  commands\n[ELSE]\n  commands\nEND IF\n</pre>\n\n<p>Blocks can be nested like in this example:</p>\n\n<pre class=\"example\">\nPRINT \"NUMBER OF PLAYERS\"\nINPUT \"1-4: \";N\n\nIF N&lt;1 OR N&gt;4 THEN\n  PRINT \"WRONG INPUT\"\n  PRINT \"RESTART!\"\nELSE IF N=1 THEN\n  PRINT \"SINGLE-PLAYER\"\nELSE\n  PRINT \"MULTI-PLAYER\"\n  IF N=4 THEN\n    PRINT \"MAXIMUM\"\n  END IF\nEND IF\n</pre>\n\n\n<h3 id=\"_GOTO\" name=\"GOTO\">GOTO</h3>\n\n<pre>GOTO label</pre>\n\n<p>Jumps to the given label and continues the program execution there.\n</p>\n\n<pre class=\"example\">\nPRINT \"START\"\nGOTO GAMELEVEL\n\nGAMEOVER:\nPRINT \"GAME OVER\"\nEND\n\nGAMELEVEL:\nPRINT \"PLAYING\"\nGOTO GAMEOVER\n</pre>\n\n\n<h3 id=\"_GOSUB\" name=\"GOSUB\">GOSUB</h3>\n\n<pre>GOSUB label</pre>\n\n<p>Adds the current program position to a stack and jumps to the\ngiven label. The program after the label is called a subroutine and\nmust be finished using RETURN.</p>\n\n<pre class=\"example\">\nFOR I=1 TO 5\n  GOSUB SHOWNUMBER\nNEXT I\nEND\n\nSHOWNUMBER:\nPRINT \"NUMBER\",I\nRETURN\n</pre>\n\n<p>NOTE: Subroutines exist mostly for historical reasons. You should\nprefer the more powerful and safer subprograms.</p>\n\n\n<h3 id=\"_RETURN\" name=\"RETURN\">RETURN</h3>\n\n<pre>RETURN</pre>\n\n<p>Jumps back to the position of the last call of GOSUB and removes\nit from the stack.</p>\n\n<pre>RETURN label</pre>\n\n<p>Works like GOTO, but clears the whole stack. Use this to exit from\na subroutine, if you want to continue your program somewhere else.</p>\n\n<pre class=\"example\">\nFOR I=1 TO 5\n  GOSUB SHOWNUMBER\nNEXT I\nEND\n\nSHOWNUMBER:\nIF I=4 THEN RETURN GAMEOVER\nPRINT \"NUMBER\",I\nRETURN\n\nGAMEOVER:\nPRINT \"GAME OVER\"\n</pre>\n\n\n<h3 id=\"_END\" name=\"END\">END</h3>\n\n<pre>END</pre>\n\n<p>Stops the program from any position. The program is also stopped\nautomatically after the last line of code.</p>\n\n<pre class=\"example\">\nPRINT \"THIS HAPPENS\"\nEND\nPRINT \"THIS DOESN'T\"\n</pre>\n\n\n<h3 id=\"_WAIT-VBL\" name=\"WAIT VBL,WAIT,VBL\">WAIT VBL</h3>\n\n<pre>WAIT VBL</pre>\n\n<p>Waits for the next frame. This (or WAIT n) should be the last\ncommand in all loops which do animations and/or handle input,\nlike the main game loop.</p>\n\n<pre class=\"example\">\nDO\n  PAL RND(3)\n  TEXT RND(19),RND(15),\"*\"\n  WAIT VBL\nLOOP\n</pre>\n\n\n<h3 id=\"_WAIT\" name=\"WAIT\">WAIT</h3>\n\n<pre>WAIT n</pre>\n\n<p>Waits <em>n</em> frames (n/60 seconds), where the minimum for\n<em>n</em> is 1.\nSubprograms running from interrupts (ON VBL/RASTER, MUSIC) will\ncontinue to work as normal during this period.</p>\n<p>WAIT 1 is the same as WAIT VBL, so why is there WAIT VBL? Because\nit looks cooler and nerdier! A little guideline: Use WAIT VBL in loops\nfor smooth animations and input handling, and WAIT n if you actually\nwant to wait some time.</p>\n\n<pre class=\"example\">\nPRINT \"INUTILIS\"\nPRINT \"PRESENTS...\"\nWAIT 60\nPRINT \"THE WAIT PROGRAM!\"\n</pre>\n\n\n<h2 id=\"Loops\">Loops</h2>\n\n<h3 id=\"_FOR-NEXT\" name=\"FOR,TO,STEP,NEXT\">FOR...NEXT</h3>\n<pre>\nFOR var=a TO b [STEP s]\n  commands\nNEXT var\n</pre>\n\n<p>Performs a series of commands in a loop a given number of times.\nThe FOR command uses the variable <em>var</em> as a counter, starting with the\nvalue <em>a</em>. All commands until NEXT are executed, then the counter is\nincreased by <em>s</em> (or 1 if STEP is omitted). A check is performed to see\nif the counter is now greater than <em>b</em>. If not, the process is repeated.\nIf it is greater, the program continues with the lines after NEXT.</p>\n\n<pre class=\"example\">\nFOR I=1 TO 8\n  PRINT I\nNEXT I\n</pre>\n\n<pre class=\"example\">\nFOR I=2 TO 16 STEP 2\n  PRINT I\nNEXT I\n</pre>\n\n<p>If STEP <em>s</em> is negative, the loop is executed until the\ncounter is less than value <em>b</em>.</p>\n\n<pre class=\"example\">\nFOR I=8 TO 1 STEP -1\n  PRINT I\nNEXT I\n</pre>\n\n<p>Loops can be placed inside of others:</p>\n\n<pre class=\"example\">\nFOR P=0 TO 3\n  PAL P\n  FOR N=1 TO 4\n    PRINT N\n  NEXT N\nNEXT P\n</pre>\n\n\n<h3 id=\"_DO-LOOP\" name=\"DO,LOOP\">DO...LOOP</h3>\n<pre>\nDO\n  commands\nLOOP\n</pre>\n\n<p>Performs commands in an endless loop. You can use GOTO or EXIT to leave it.\n</p>\n\n<pre class=\"example\">\nDO\n  PAL RND(3)\n  PRINT \"FOREVER\"\nLOOP\n</pre>\n\n\n<h3 id=\"_REPEAT-UNTIL\" name=\"REPEAT,UNTIL\">REPEAT...UNTIL</h3>\n<pre>\nREPEAT\n  commands\nUNTIL expression\n</pre>\n\n<p>Executes the commands in a loop until the given expression is true.\nThe loop is executed at least once.</p>\n\n<pre class=\"example\">\nREPEAT\n  A=RND(9)\n  PRINT A\nUNTIL A=0\n</pre>\n\n\n<h3 id=\"_WHILE-WEND\" name=\"WHILE,WEND\">WHILE...WEND</h3>\n<pre>\nWHILE expression\n  commands\nWEND\n</pre>\n\n<p>Executes the commands in a loop as long as the given expression is\ntrue.</p>\n\n<pre class=\"example\">\nA=4\nWHILE A&lt;10\n  PRINT A\n  A=A+1\nWEND\n</pre>\n\n\n<h3 id=\"_EXIT\" name=\"EXIT\">EXIT</h3>\n<pre>\nEXIT\n</pre>\n\n<p>Exits immediately from a loop created with the FOR...NEXT, REPEAT...UNTIL, WHILE...WEND, or DO...LOOP commands.</p>\n\n<pre class=\"example\">\nDO\n  INPUT \"ENTER E TO EXIT:\";A$\n  IF A$=\"E\" THEN EXIT\n  PRINT \"GOING ON...\"\nLOOP\nPRINT \"END\"\n</pre>\n\n\n<h2 id=\"Subprograms\">Subprograms</h2>\n\n<h3 id=\"_SUB\" name=\"SUB,END SUB,END\">SUB...END SUB</h3>\n<pre>\nSUB name [(parameter-list)]\n  commands\nEND SUB\n</pre>\n\n<p>Defines a subprogram with the given name. The optional parameter\nlist can contain two types of entries: simple variables and array\nvariables (followed by an empty parentheses pair). Entries are\nseparated by commas. By default all variables inside the subprogram\nare local.</p>\n\n<p>NOTE: Don't use GOTO or GOSUB to jump out of a subprogram!</p>\n\n\n<h3 id=\"_CALL\" name=\"CALL\">CALL</h3>\n<pre>\nCALL name [(argument-list)]\n</pre>\n\n<p>Executes the subprogram with the given name and returns to the\ncurrent position after finishing it. The argument list\nmust match the parameters of the SUB definition. Simple variables,\nsingle array elements and entire arrays (followed by an empty parentheses pair) are passed by reference to the subprogram. Other\nexpressions are passed by value.</p>\n\n<pre class=\"example\">\nCALL GREET(\"LOWRES NX\",3)\nCALL GREET(\"YOU\",1)\nCALL BYE\n\nSUB BYE\n  PAL 1\n  PRINT \"BYE\"\nEND SUB\n\nSUB GREET(N$,A)\n  FOR I=1 TO A\n    PAL RND(3)\n    PRINT \"HELLO\",N$\n  NEXT I\nEND SUB\n</pre>\n\n\n<h3 id=\"_EXIT-SUB\" name=\"EXIT SUB,EXIT,SUB\">EXIT SUB</h3>\n<pre>\nEXIT SUB\n</pre>\n\n<p>Exits a subprogram before END SUB is reached.</p>\n\n\n<h3 id=\"_GLOBAL\" name=\"GLOBAL\">GLOBAL</h3>\n<pre>\nGLOBAL variable-list\n</pre>\n\n<p>Makes variables from the main program available to all subprograms.\nThe list can contain simple variables only. For arrays you should use\nDIM GLOBAL. This command cannot be used within a subprogram.</p>\n\n<pre class=\"example\">\nGLOBAL SCORE,TRIES\n\nTRIES=3\n\nCALL WIN(10)\nCALL WIN(20)\n\nSUB WIN(N)\n  SCORE=SCORE+N\n  PRINT \"SCORE:\",SCORE,\"TRIES:\",TRIES\nEND SUB\n</pre>\n\n\n<h1 id=\"Text\">Text</h1>\n\n<h3 id=\"_PRINT\" name=\"PRINT\">PRINT</h3>\n\n<pre>PRINT expression-list</pre>\n\n<p>Outputs text to the current window. Expressions can be strings or\nnumbers, separated by commas or semicolons. A comma separates the\noutput with a space, a semicolon outputs without space.\nEnd the list with a comma or semicolon to keep the cursor at the\nend of the output, otherwise a new line is started.</p>\n\n<pre class=\"example\">\nPRINT \"HELLO WORLD\"\nPRINT 42\nPRINT 1+3\nPRINT \"SCORE:\",100\nPRINT \"ONE \";\nPRINT \"LINE\"\n</pre>\n\n\n<h3 id=\"_INPUT\" name=\"INPUT\">INPUT</h3>\n\n<pre>INPUT [\"prompt\";]var</pre>\n\n<p>Lets the user enter a text or number on the keyboard and stores it\nin the variable <em>var</em>. Optionally it can show a prompt text before\n(cannot be a variable).</p>\n<p>INPUT automatically enables the keyboard.</p>\n\n<pre class=\"example\">\nINPUT \"NAME:\";N$\nINPUT \"AGE:\";AGE\nPRINT \"HELLO \";N$;\"!\"\nPRINT \"SOON YOU ARE \";AGE+1\n</pre>\n\n\n<h3 id=\"_LOCATE\" name=\"LOCATE\">LOCATE</h3>\n\n<pre>LOCATE cx,cy</pre>\n\n<p>Moves the text cursor to column <em>cx</em> and row <em>cy</em>\nrelative to the current window.</p>\n\n<pre class=\"example\">\nLOCATE 2,4\nPRINT \"HELLO\"\n</pre>\n\n\n<h3 id=\"_CURSOR.X-CURSOR.Y\" name=\"CURSOR.X,CURSOR.Y\">=CURSOR.X/Y</h3>\n<pre>\nCURSOR.X\nCURSOR.Y\n</pre>\n\n<p>Returns the current column (X) or row (Y) of the text cursor\nrelative to the current window.</p>\n\n\n<h3 id=\"_WINDOW\" name=\"WINDOW\">WINDOW</h3>\n\n<pre>WINDOW cx,cy,w,h,b</pre>\n\n<p>Sets the text output window to cell position <em>cx,cy</em> and sets the size\nto <em>w</em> columns and <em>h</em> rows. Text will be written to background <em>b</em> (0\nor 1).</p>\n\n<pre class=\"example\">\nWINDOW 6,4,8,4,0\nPRINT \"LONG TEXT IN A SMALL WINDOW\"\n</pre>\n\n\n<h3 id=\"_CLW\" name=\"CLW\">CLW</h3>\n\n<pre>CLW</pre>\n\n<p>Clears the window with spaces and resets the text cursor position.\n</p>\n\n<pre class=\"example\">\nPRINT \"HELLO\"\nCLW\nPRINT \"BYE\"\n</pre>\n\n\n<h1 id=\"User-Input\">User Input</h1>\n\n<h2 id=\"Gamepads\">Gamepads</h2>\n\n<h3 id=\"_GAMEPAD\" name=\"GAMEPAD\">GAMEPAD</h3>\n    \n<pre>GAMEPAD n</pre>\n\n<p>Enables gamepads for <em>n</em> (1 or 2) players. Once the gamepad is\nenabled, the program cannot change to touchscreen/mouse input anymore.\n</p>\n\n\n<h3 id=\"_UP-DOWN-LEFT-RIGHT\" name=\"=UP,DOWN,LEFT,RIGHT\">=UP/DOWN/LEFT/RIGHT</h3>\n\n<pre>\nUP(p)\nDOWN(p)\nLEFT(p)\nRIGHT(p)\n</pre>\n\n<p>Returns true if the given direction is currently pressed on the\ndirection pad of player <em>p</em> (0/1).</p>\n\n<pre>\nUP TAP(p)\nDOWN TAP(p)\nLEFT TAP(p)\nRIGHT TAP(p)\n</pre>\n\n<p>With the optional TAP keyword, this function returns true only for\nthe first frame the button is pressed.</p>\n\n<pre class=\"example\">\nGAMEPAD 1\nDO\n  IF UP(0) THEN PRINT \"UP\"\n  IF DOWN(0) THEN PRINT \"DOWN\"\n  IF LEFT TAP(0) THEN PRINT \"TAP &lt;\"\n  IF RIGHT TAP(0) THEN PRINT \"TAP &gt;\"\n  WAIT VBL\nLOOP\n</pre>\n\n\n<h3 id=\"_BUTTON\" name=\"BUTTON,TAP\">=BUTTON</h3>\n\n<pre>BUTTON(p[,n])</pre>\n\n<p>Returns true if button A (<em>n</em>=0) or B (<em>n</em>=1) is currently pressed by\nplayer <em>p</em> (0/1). If the parameter <em>n</em> is omitted, both buttons\n(A and B) are checked.</p>\n\n<pre>BUTTON TAP(p[,n])</pre>\n\n<p>With the optional TAP keyword, this function returns true only for\nthe first frame the button is pressed.</p>\n\n<pre class=\"example\">\nGAMEPAD 1\nDO\n  IF BUTTON(0,0) THEN PRINT \"A\"\n  IF BUTTON TAP(0,1) THEN PRINT \"TAP B\"\n  WAIT VBL\nLOOP\n</pre>\n\n\n<h3 id=\"_PAUSE-ON-OFF\" name=\"PAUSE ON,PAUSE OFF,PAUSE,ON,OFF\">PAUSE ON/OFF</h3>\n\n<pre>\nPAUSE ON\nPAUSE OFF\n</pre>\n\n<p>Enables or disables the automatic pause handling. By default it's\nenabled, so if you press the pause button, the program stops and\nshows \"PAUSE\" on the screen, until the button is pressed again.</p>\n\n\n<h3 id=\"_PAUSE\" name=\"PAUSE\">PAUSE</h3>\n\n<pre>\nPAUSE\n</pre>\n\n<p>Pauses the program and shows the default \"PAUSE\" screen, even if\nautomatic pause handling is disabled.</p>\n\n\n<h3 id=\"_=PAUSE\" name=\"PAUSE\">=PAUSE</h3>\n\n<pre>\nPAUSE\n</pre>\n\n<p>Returns true if the pause button was pressed, otherwise false.\nAfter calling this function its value is cleared, so it returns\neach button tap only once. The automatic pause handling needs to\nbe disabled for this function.</p>\n\n<pre class=\"example\">\nGAMEPAD 1\nPAUSE OFF\nDO\n  IF PAUSE THEN PRINT \"PAUSE PRESSED\"\n  WAIT VBL\nLOOP\n</pre>\n\n\n<h2 id=\"Touchscreen-Mouse\">Touchscreen/Mouse</h2>\n\n<p>Use touchscreen support only if you think it will work well with a\ncomputer mouse, too. If you want to create your own game buttons,\nkeep in mind that your game might be unplayable on a computer,\nbecause it won't support the keyboard or a real gamepad. Always\nconsider using the standard gamepad functions.</p>\n\n\n<h3 id=\"_TOUCHSCREEN\" name=\"TOUCHSCREEN\">TOUCHSCREEN</h3>\n\n<pre>\nTOUCHSCREEN\n</pre>\n\n<p>Enables the touchscreen/mouse support. Once it's enabled, the\nprogram cannot change to gamepad input anymore.</p>\n\n\n<h3 id=\"_TOUCH.X-TOUCH.Y\" name=\"TOUCH.X,TOUCH.Y\">=TOUCH.X/Y</h3>\n<pre>\nTOUCH.X\nTOUCH.Y\n</pre>\n\n<p>Returns the current X or Y pixel position where the user touches\nthe screen, or where it was touched the last time.</p>\n\n\n<h3 id=\"_TOUCH\" name=\"TOUCH\">=TOUCH</h3>\n\n<pre>TOUCH</pre>\n\n<p>Returns true if the screen is currently touched.</p>\n\n<pre class=\"example\">\nTOUCHSCREEN\nDO\n  IF TOUCH THEN PRINT TOUCH.X,TOUCH.Y\n  WAIT VBL\nLOOP\n</pre>\n\n\n<h3 id=\"_TAP\" name=\"TAP\">=TAP</h3>\n\n<pre>TAP</pre>\n\n<p>Returns true if the screen is currently touched and was not\ntouched the last frame.</p>\n\n<pre class=\"example\">\nTOUCHSCREEN\nDO\n  IF TAP THEN PRINT TOUCH.X,TOUCH.Y\n  WAIT VBL\nLOOP\n</pre>\n\n\n<h2 id=\"Keyboard\">Keyboard</h2>\n\n<h3 id=\"_KEYBOARD-ON-OFF-OPTIONAL\" name=\"KEYBOARD ON,KEYBOARD OFF,KEYBOARD OPTIONAL,KEYBOARD,ON,OFF,OPTIONAL\">KEYBOARD ON/OFF/OPTIONAL</h3>\n\n<pre>\nKEYBOARD ON\nKEYBOARD OFF\n</pre>\n\n<p>Enables or disables the keyboard. While the keyboard is enabled,\ngamepads don't work.</p>\n\n<pre>\nKEYBOARD OPTIONAL\n</pre>\n\n<p>Enables the keyboard, but won't show an on-screen keyboard on\ntouchscreen devices. Programs using this mode should be completely\nusable with gamepad or touch control and use the keyboard for\nalternative input only.</p>\n\n\n<h3 id=\"_INKEY$\" name=\"INKEY$\">=INKEY$</h3>\n\n<pre>INKEY$</pre>\n\n<p>Returns the last pressed key as a string. If no key was pressed,\nit returns an empty string (\"\"). After calling this function its\nvalue is cleared, so it returns each pressed key only once.\nThe keyboard needs to be enabled for this function.</p>\n\n<pre class=\"example\">\nKEYBOARD ON\nDO\n  I$=INKEY$\n  IF I$&lt;&gt;\"\" THEN PRINT I$\n  WAIT VBL\nLOOP\n</pre>\n\n\n<h1 id=\"Graphics\">Graphics</h1>\n\n<p>All graphics in LowRes NX are based on characters. A character is an\n8x8-pixel image with 3 colors plus transparent. It's displayed with\none of the 8 programmable color palettes.</p>\n\n<p>At program start all characters from ROM entry 2 are copied to\nvideo RAM to make them immediately usable.</p>\n\n<p>The display is composed of 3 layers, which are from back to front:</p>\n<ul>\n<li>Background 1 (BG 1)</li>\n<li>Background 0 (BG 0)</li>\n<li>Sprites</li>\n</ul>\n\n<p>Each sprite and background cell has an attribute called \"priority\".\nBy setting it, the cell or sprite will appear on a higher display\nlayer. Actually there are 6 layers, from back to front:</p>\n<ul>\n<li>Background 1 (BG 1) - prio 0</li>\n<li>Background 0 (BG 0) - prio 0</li>\n<li>Sprites - prio 0</li>\n<li>Background 1 (BG 1) - prio 1</li>\n<li>Background 0 (BG 0) - prio 1</li>\n<li>Sprites - prio 1</li>\n</ul>\n\n\n<h2 id=\"Sprites\">Sprites</h2>\n\n<p>Sprites are independent objects, which can be freely moved on the\nscreen. They can have a size of 8x8 pixels (one character) or up to\n32x32 pixels by grouping several characters. Each sprite has the\nstandard character attributes (color palette, flip X/Y, priority) and additionally its size.</p>\n\n\n<h3 id=\"_SPRITE\" name=\"SPRITE,PAL,FLIP,PRIO\">SPRITE</h3>\n\n<pre>SPRITE n,[x],[y],[c]</pre>\n\n<p>Sets the position <em>(x,y)</em> and character <em>(c)</em> of sprite <em>n</em> (0-63).\nAll parameters can be omitted to keep their current settings.</p>\n\n<pre>\nSPRITE n [PAL pal] [FLIP fx,fy]\n... [PRIO pri] [SIZE s]\n</pre>\n\n<p>Sets one or more attributes of sprite <em>n</em> (0 - 63):</p>\n\n<table>\n<tr><td>pal</td> <td>palette number (0-7)</td></tr>\n<tr><td>fx</td> <td>flip horizontally (0/1)</td></tr>\n<tr><td>fy</td> <td>flip vertically (0/1)</td></tr>\n<tr><td>pri</td> <td>priority (0/1)</td></tr>\n<tr><td>s</td> <td>size (0-3):<br/>\n0: 1 character (8x8 px)<br/>\n1: 2x2 characters (16x16 px)<br/>\n2: 3x3 characters (24x24 px)<br/>\n3: 4x4 characters (32x32 px)</td></tr>\n</table>\n\n<pre class=\"example\">\nSPRITE 0,32,64,193\nSPRITE 1,64,64,193\nSPRITE 1 PAL 1 SIZE 3\nFOR X=64 TO 128\n  SPRITE 1,X,,\n  IF X=96 THEN SPRITE 1 FLIP 1,0\n  WAIT VBL\nNEXT X\n</pre>\n\n\n<h3 id=\"_SPRITE.A\" name=\"SPRITE.A\">SPRITE.A</h3>\n\n<pre>SPRITE.A n,a</pre>\n\n<p>Sets all attributes of sprite <em>n</em> (0-63) as a single 8-bit value.\nSee <a href=\"#Sprite-Registers\">\"Sprite Registers\"</a> for more information.</p>\n\n<p>NOTE: It's easier to set these values with the attributes syntax of the\n<a href=\"#_SPRITE\">SPRITE</a> command.</p>\n\n\n<h3 id=\"_SPRITE-OFF\" name=\"SPRITE OFF,SPRITE,OFF\">SPRITE OFF</h3>\n\n<pre>\nSPRITE OFF [n]\nSPRITE OFF a TO b\n</pre>\n\n<p>Hides one or more sprites. If all parameters are omitted, all sprites (0 - 63) are hidden. With one parameter only the given sprite is hidden. The last option is to hide sprites in the range from <em>a</em> to <em>b</em>.</p>\n\n\n<h3 id=\"_=SPRITE.X-Y\" name=\"SPRITE.X,SPRITE.Y\">=SPRITE.X/Y</h3>\n\n<pre>\nSPRITE.X(n)\nSPRITE.Y(n)\n</pre>\n\n<p>Return the position of sprite <em>n</em>.</p>\n\n\n<h3 id=\"_=SPRITE.C\" name=\"SPRITE.C\">=SPRITE.C</h3>\n\n<pre>SPRITE.C(n)</pre>\n\n<p>Returns the character of sprite <em>n</em>.</p>\n\n\n<h3 id=\"_=SPRITE.A\" name=\"SPRITE.A\">=SPRITE.A</h3>\n\n<pre>SPRITE.A(n)</pre>\n\n<p>Returns the attributes of sprite <em>n</em> as an 8-bit value.\nSee <a href=\"#Sprite-Registers\">\"Sprite Registers\"</a> for more\ninformation.</p>\n\n\n<h3 id=\"_=SPRITE-HIT\" name=\"SPRITE HIT,SPRITE,HIT\">=SPRITE HIT</h3>\n\n<pre>SPRITE HIT(n[,a [TO b]])</pre>\n\n<p>Returns true if sprite <em>n</em> collides with another sprite (which means\nthat pixels overlap). If no more parameters are given, it will\ncheck with all other visible sprites. If the <em>a</em> parameter is added,\nit will check only with that sprite <em>a</em>. If all parameters are\ngiven, it will check with all sprites from number <em>a</em> to number <em>b</em>.</p>\n\n<pre class=\"example\">\nSPRITE 0,32,64,225\nSPRITE 1,96,68,226\nFOR X=32 TO 128\n  SPRITE 0,X,,\n  IF SPRITE HIT(0,1) THEN\n    SPRITE 0 PAL 1\n  ELSE\n    SPRITE 0 PAL 0\n  END IF\n  WAIT VBL\nNEXT X\n</pre>\n\n\n<h3 id=\"_=HIT\" name=\"HIT\">=HIT</h3>\n\n<pre>HIT</pre>\n\n<p>Returns the number of the sprite which collided with the sprite of\nthe last call of SPRITE HIT.</p>\n\n\n<h2 id=\"Backgrounds\">Backgrounds</h2>\n\n<p>A background is a map of 32x32 character cells, which is used for text\nand tile based maps or images. Each cell has the information of which\ncharacter it contains and additional attributes (color palette, flip\nX/Y, priority).</p>\n\n<p>As a character has the size of 8x8 pixels, the resulting background\nsize is 256x256 pixels, which is larger than the actual screen\n(160x128). By modifying the scroll offset of a background, the visible\narea can be moved.</p>\n\n<p>If the visible area moves out of the borders of the background, the\ndisplay wraps around the edges. This can be used to achieve endless\nscrolling.</p>\n\n<p>There is a mode for 16x16-pixel cells. When active, each cell will show\n2x2 characters, similar to big sprites. This mode also increases the\nbackground size to 512x512 pixels. Use the CELL SIZE command to enable\nit.</p>\n\n<p>For most of the commands and functions that access backgrounds\ntheir cell co-ordinates can be outside of the background size (32x32).\nThey will be wrapped around the edges, so for example a character drawn\nat position 34,-2 will actually appear at position 2,30.</p>\n\n\n<h3 id=\"_CLS\" name=\"CLS\">CLS</h3>\n    \n<pre>CLS</pre>\n\n<p>Clears both backgrounds with character 0 and resets the current\nwindow to the default one.</p>\n\n<pre>CLS b</pre>\n\n<p>Clears background <em>b</em> with character 0.</p>\n\n\n<h3 id=\"_PAL\" name=\"PAL\">PAL</h3>\n\n<pre>PAL p</pre>\n\n<p>Sets the palette number (0-7) attribute for cell and text commands.</p>\n\n<pre class=\"example\">\nPRINT \"HELLO\"\nPAL 1\nPRINT \"LOWRES NX!\"\n</pre>\n\n\n<h3 id=\"_FLIP\" name=\"FLIP\">FLIP</h3>\n\n<pre>FLIP fx,fy</pre>\n\n<p>Sets the values (0 or 1) for horizontal (<em>fx</em>) and vertical (<em>fy</em>)\nflip attributes for cell and text commands.</p>\n\n<pre class=\"example\">\nPRINT \"LOWRES NX\"\nFLIP 0,1\nPRINT \"LOWRES NX\"\nFLIP 1,0\nPRINT \"XN SERWOL\"\n</pre>\n\n\n<h3 id=\"_PRIO\" name=\"PRIO\">PRIO</h3>\n\n<pre>PRIO p</pre>\n\n<p>Sets the priority (0 or 1) attribute for cell and text commands.</p>\n\n\n<h3 id=\"_ATTR\" name=\"ATTR\">ATTR</h3>\n\n<pre>ATTR a</pre>\n\n<p>Sets all attributes for cell and text commands as a single\n8-bit value. See <a href=\"#BG-Data\">\"BG Data\"</a> for more information.</p>\n\n<p>NOTE: It's easier to use the commands <a href=\"#_PAL\">PAL</a>, <a href=\"#_FLIP\">FLIP</a> and <a href=\"#_PRIO\">PRIO</a>.</p>\n\n\n<h3 id=\"_BG\" name=\"BG\">BG</h3>\n\n<pre>BG n</pre>\n\n<p>Sets the current background (0 or 1) for cell and text commands.</p>\n\n\n<h3 id=\"_CELL\" name=\"CELL\">CELL</h3>\n\n<pre>CELL cx,cy,c</pre>\n\n<p>Sets the cell at position <em>cx,cy</em> of the current background to\ncharacter <em>c</em> using the current attributes.</p>\n\n<pre class=\"example\">\nPAL 2\nCELL 1,1,225\nCELL 2,1,226\nPAL 1\nCELL 3,1,227\n</pre>\n\n\n<h3 id=\"_CELL.C\" name=\"CELL.C\">=CELL.C</h3>\n\n<pre>CELL.C(cx,cy)</pre>\n\n<p>Returns the character of the cell at position <em>cx,cy</em> of the current\nbackground.</p>\n\n\n<h3 id=\"_CELL.A\" name=\"CELL.A\">=CELL.A</h3>\n\n<pre>CELL.A(cx,cy)</pre>\n\n<p>Returns the attributes of the cell at position <em>cx,cy</em> of the current\nbackground as an 8-bit value. See <a href=\"#BG-Data\">\"BG Data\"</a> for\nmore information.</p>\n\n\n<h3 id=\"_BG-FILL\" name=\"BG FILL,BG,FILL,TO,CHAR\">BG FILL</h3>\n\n<pre>BG FILL cx1,cy1 TO cx2,cy2 CHAR c</pre>\n\n<p>Sets all cells in the area from <em>cx1,cy1</em> to <em>cx2,cy2</em>\nof the current background to character <em>c</em> using the current attributes.</p>\n\n<pre class=\"example\">\nPAL 2\nBG FILL 2,2 TO 17,13 CHAR 225\nPAL 1\nBG FILL 10,8 TO 17,13 CHAR 226\n</pre>\n\n\n<h3 id=\"_TINT\" name=\"TINT,PAL,FLIP,PRIO\">TINT</h3>\n\n<pre>\nTINT cx,cy [PAL pal]\n... [FLIP fx,fy] [PRIO pri]\n</pre>\n\n<p>Changes the palette and/or other attributes of the cell at position\n<em>cx,cy</em> of the current background. The cell's character and\nomitted attributes stay unchanged.</p>\n\n<pre class=\"example\">\nBG FILL 2,2 TO 17,13 CHAR 225\nTINT 10,8 PAL 1 FLIP 0,1\n</pre>\n\n\n<h3 id=\"_BG-TINT\" name=\"BG TINT,BG,TINT,TO,PAL,FLIP,PRIO\">BG TINT</h3>\n\n<pre>\nBG TINT cx1,cy1 TO cx2,cy2\n... [PAL pal] [FLIP fx,fy] [PRIO pri]\n</pre>\n\n<p>Changes the palette and/or other attributes of all cells in the\narea from <em>cx1,cy1</em> to <em>cx2,cy2</em> of the current\nbackground. The cells' characters and omitted attributes stay\nunchanged.</p>\n\n<pre class=\"example\">\nBG FILL 2,2 TO 17,13 CHAR 225\nBG TINT 10,8 TO 15,11 PAL 1\n</pre>\n\n\n<h3 id=\"_BG-SOURCE\" name=\"BG SOURCE,BG,SOURCE\">BG SOURCE</h3>\n\n<pre>BG SOURCE a[,w,h]</pre>\n\n<p>Sets the current source for the BG COPY command. The two-dimensional\nmap starts at memory address <em>a</em>, has a width of <em>w</em> and\na height of <em>h</em> cells.</p>\n\n<p>Without the size parameters, Gfx Designer's background data\nformat is assumed: The width is read from address <em>a</em>+2, the\nheight from <em>a</em>+3 and the actual map data starts at <em>a</em>+4.</p>\n\n<p>By default ROM entry 3 is used as source.</p>\n\n<pre class=\"example\">\n'USE A MAP FROM ROM ENTRY 4\nBG SOURCE ROM(4)\n'USE A MAP FROM WORKING RAM\nBG SOURCE $A000,32,32\n</pre>\n\n\n<h3 id=\"_BG-COPY\" name=\"BG COPY,BG,COPY\">BG COPY</h3>\n\n<pre>BG COPY cx1,cy1,w,h TO cx2,cy2</pre>\n\n<p>Copies a two-dimensional part of the current source to the\ncurrent background.</p>\n\n<pre class=\"example\">\n'SHOW SCREEN FROM GFX DESIGNER\nBG COPY 0,0,20,16 TO 0,0\n</pre>\n\n\n<h3 id=\"_BG-SCROLL\" name=\"BG SCROLL,BG,SCROLL,STEP\">BG SCROLL</h3>\n\n<pre>BG SCROLL cx1,cy1 TO cx2,cy2 STEP dx,dy</pre>\n\n<p>Moves the content of all cells in the area from <em>cx1,cy1</em> to <em>cx2,cy2</em>\nhorizontally by <em>dx</em> and vertically by <em>dy</em> cells.</p>\n\n<pre class=\"example\">\nDO\n  BG SCROLL 0,0 TO 19,15 STEP -1,0\n  CELL 18,RND(15),193+RND(62)\n  WAIT 5\nLOOP\n</pre>\n\n\n<h3 id=\"_MCELL.C-MCELL.A\" name=\"MCELL.C,MCELL.A,MCELL\">=MCELL.C/A</h3>\n\n<pre>MCELL.C(cx,cy)</pre>\n<pre>MCELL.A(cx,cy)</pre>\n\n<p>Work like the CELL.C and CELL.A functions, but get a cell from the\nsource map (BG SOURCE) instead of the current background. If the\nco-ordinates are outside of the map bounds, the functions return -1.</p>\n\n\n<h3 id=\"_MCELL\" name=\"MCELL\">MCELL</h3>\n\n<pre>MCELL cx,cy,c</pre>\n\n<p>Works like the CELL command, but sets a cell in the source map\n(BG SOURCE) instead of the current background. The source must be in\nworking RAM, otherwise you will get an \"Illegal Memory Access\" error.</p>\n\n\n<h3 id=\"_TEXT\" name=\"TEXT\">TEXT</h3>\n\n<pre>TEXT cx,cy,s$</pre>\n\n<p>Outputs the string <em>s$</em> to the current background at cell position\n<em>cx,cy</em> using the current attributes.</p>\n\n<pre class=\"example\">\nPAL 3\nTEXT 15,15,\"HELLO\"\n</pre>\n\n\n<h3 id=\"_NUMBER\" name=\"NUMBER\">NUMBER</h3>\n\n<pre>NUMBER cx,cy,n,d</pre>\n\n<p>Outputs the number <em>n</em> to the current background at cell position\n<em>cx,cy</em> using the current attributes. The number is formatted to show\nalways <em>d</em> digits.\nThis command is preferred over TEXT to show numbers, as it\ndoesn't need to convert numbers to strings.</p>\n\n<pre class=\"example\">\nNUMBER 15,0,321,5\n</pre>\n\n\n<h3 id=\"_FONT\" name=\"FONT\">FONT</h3>\n    \n<pre>FONT c</pre>\n\n<p>Sets the current character range used for text output.\n<em>c</em> is the character where the font starts (space).</p>\n\n<p>The default value is 192, which points to the standard font,\nif available.</p>\n\n\n<h3 id=\"_SCROLL\" name=\"SCROLL\">SCROLL</h3>\n\n<pre>SCROLL b,x,y</pre>\n\n<p>Sets the scroll offset of background <em>b</em> (0/1) to pixel\nco-ordinates <em>x,y</em>.</p>\n\n<pre class=\"example\">\nTEXT 0,7,\"SCROLLING BACKGROUND\"\nFOR I=0 TO 256\n  SCROLL 0,I,0\n  WAIT VBL\nNEXT I\n</pre>\n\n\n<h3 id=\"_SCROLL.X-SCROLL.Y\" name=\"SCROLL.X,SCROLL.Y\">=SCROLL.X/Y</h3>\n\n<pre>\nSCROLL.X(b)\nSCROLL.Y(b)\n</pre>\n\n<p>Return the scroll offset of background <em>b</em>.</p>\n\n\n<h2 id=\"Display-Settings\">Display Settings</h2>\n\n<h3 id=\"_SPRITE-VIEW\" name=\"SPRITE VIEW ON,SPRITE VIEW OFF,SPRITE,VIEW,ON,OFF,SPRITE VIEW\">SPRITE VIEW ON/OFF</h3>\n\n<pre>SPRITE VIEW ON</pre>\n<pre>SPRITE VIEW OFF</pre>\n\n<p>Shows or hides the complete sprite layer without changing any of\nthe sprites' settings.</p>\n\n\n<h3 id=\"_BG-VIEW\" name=\"BG VIEW ON,BG VIEW OFF,BG,VIEW,ON,OFF,BG VIEW\">BG VIEW ON/OFF</h3>\n\n<pre>BG VIEW ON n</pre>\n<pre>BG VIEW OFF n</pre>\n\n<p>Shows or hides background <em>n</em> (0 or 1).</p>\n\n\n<h3 id=\"_CELL-SIZE\" name=\"CELL SIZE,CELL,SIZE\">CELL SIZE</h3>\n\n<pre>CELL SIZE b,s</pre>\n\n<p>Sets the cell size for background <em>b</em> (0 or 1), where <em>s</em>\ncan be:</p>\n\n<table>\n<tr><td>0</td> <td>1 character (8x8 pixels)</td></tr>\n<tr><td>1</td> <td>2x2 characters (16x16 pixels)</td></tr>\n</table>\n\n\n<h3 id=\"_PALETTE\" name=\"PALETTE\">PALETTE</h3>\n\n<pre>PALETTE n,[c0],[c1],[c2],[c3]</pre>\n\n<p>Sets all four colors of palette <em>n</em> (0-7). Color 0 is only used for\npalette 0 and shown as the screen's backdrop color. The color\nparameters can be omitted to keep their current settings.\nValid color values are 0-63 and can be calculated like this:</p>\n<pre>\nVALUE = RED * 16 + GREEN * 4 + BLUE\n</pre>\n<p>RED, GREEN and BLUE are values from 0 to 3.</p>\n\n<p>By default all palettes are read from ROM entry 1.</p>\n\n<pre class=\"example\">\nPRINT \"COLORS!\"\nWAIT 30\nPALETTE 0,48,63,63,0\nWAIT 30\nPALETTE 0,3,,,\nWAIT 30\nPALETTE 0,12,,,8\n</pre>\n\n\n<h3 id=\"_=COLOR\" name=\"COLOR\">=COLOR</h3>\n\n<pre>COLOR(p,n)</pre>\n\n<p>Returns the value of color <em>n</em> (0-3) from palette <em>p</em> (0-7).\nYou can get the RED, GREEN and BLUE values like this:</p>\n<pre>\nRED = INT(VALUE / 16)\nGREEN = INT(VALUE / 4) MOD 4\nBLUE = VALUE MOD 4\n</pre>\n\n\n<h3 id=\"_ON-RASTER-CALL\" name=\"ON RASTER CALL,ON RASTER,RASTER CALL,ON,RASTER,CALL,ON RASTER OFF,RASTER OFF,OFF\">ON RASTER CALL/OFF</h3>\n\n<pre>ON RASTER CALL name</pre>\n\n<p>Sets a subprogram which is executed for every screen line before\nit's drawn. Usually used to change color palettes or scroll\noffsets to achieve graphical effects. Raster subprograms should be\nshort (see <a href=\"#CPU-Cycles\">\"CPU Cycles\"</a>).</p>\n\n<pre>ON RASTER OFF</pre>\n\n<p>Removes the current subprogram.</p>\n\n<pre class=\"example\">\nON RASTER CALL RAINBOW\nPRINT \"RAINBOW WRITER\"\nINPUT A$\n\nSUB RAINBOW\n  PALETTE 0,(RASTER+TIMER) MOD 64,,,\nEND SUB\n</pre>\n\n\n<h3 id=\"_=RASTER\" name=\"RASTER\">=RASTER</h3>\n\n<pre>RASTER</pre>\n\n<p>Returns the current screen line (y position). Use this in a raster\nsubprogram.</p>\n\n\n<h3 id=\"_ON-VBL-CALL\" name=\"ON VBL CALL,ON VBL,VBL CALL,ON,VBL,CALL,ON VBL OFF,VBL OFF,OFF\">ON VBL CALL/OFF</h3>\n\n<pre>ON VBL CALL name</pre>\n\n<p>Sets a subprogram which is executed each frame. Can be used\nto update animations or sounds, even if the main program is\nblocked by WAIT or INPUT. VBL subprograms should not be very long\n(see <a href=\"#CPU-Cycles\">\"CPU Cycles\"</a>).</p>\n\n<pre>ON VBL OFF</pre>\n\n<p>Removes the current subprogram.</p>\n\n<pre class=\"example\">\nON VBL CALL BLINK\nPRINT \"BLINK WRITER\"\nINPUT A$\n\nSUB BLINK\n  PALETTE 0,TIMER MOD 64,,,\nEND SUB\n</pre>\n\n\n<h3 id=\"_=TIMER\" name=\"TIMER\">=TIMER</h3>\n\n<pre>TIMER</pre>\n\n<p>Returns the number of frames shown since LowRes NX was started.\nThe value wraps to 0 when 5184000 is reached, which is about 24\nhours.</p>\n\n\n<h1 id=\"Sound\">Sound</h1>\n\n<p>LowRes NX has four independent sound generators (voices). Each one\ncan play sawtooth, triangle, pulse and noise waveforms, and has\nfrequency, volume and pulse width settings. An additional envelope\ngenerator and LFO per voice makes complex sounds and instruments\npossible.</p>\n<p>You can use the tool <a href=\"#Sound-Composer\">\"Sound Composer\"</a> (or compatible programs) to\ncreate music, tracks and sound presets.</p>\n\n\n<h3 id=\"_MUSIC\" name=\"MUSIC\">MUSIC</h3>\n\n<pre>MUSIC [p]</pre>\n\n<p>Starts playback of a song at pattern <em>p</em>. If the parameter <em>p</em> is\nomitted, it starts at pattern 0.</p>\n\n\n<h3 id=\"_TRACK\" name=\"TRACK\">TRACK</h3>\n\n<pre>TRACK n,v</pre>\n\n<p>Plays track <em>n</em> once on voice <em>v</em>. Each voice can play a track\nindependently, so this can be used for sound effects, even while music\nis playing.</p>\n\n\n<h3 id=\"_PLAY\" name=\"PLAY\">PLAY</h3>\n\n<pre>PLAY v,p[,len] [SOUND s]</pre>\n\n<p>Plays a sound on voice <em>v</em> (0-3). <em>p</em> is the pitch:</p>\n<table class=\"bigtable\">\n    <tr><th>Note</th><th colspan=\"8\">Pitch (with different octaves)</th></tr>\n    <tr><th>C</th><td>1</td><td>13</td><td>25</td><td>37</td><td>49</td><td>61</td><td>73</td><td>85</td></tr>\n    <tr><th>C#</th><td>2</td><td>14</td><td>26</td><td>38</td><td>50</td><td>62</td><td>74</td><td>86</td></tr>\n    <tr><th>D</th><td>3</td><td>15</td><td>27</td><td>39</td><td>51</td><td>63</td><td>75</td><td>87</td></tr>\n    <tr><th>D#</th><td>4</td><td>16</td><td>28</td><td>40</td><td>52</td><td>64</td><td>76</td><td>88</td></tr>\n    <tr><th>E</th><td>5</td><td>17</td><td>29</td><td>41</td><td>53</td><td>65</td><td>77</td><td>89</td></tr>\n    <tr><th>F</th><td>6</td><td>18</td><td>30</td><td>42</td><td>54</td><td>66</td><td>78</td><td>90</td></tr>\n    <tr><th>F#</th><td>7</td><td>19</td><td>31</td><td>43</td><td>55</td><td>67</td><td>79</td><td>91</td></tr>\n    <tr><th>G</th><td>8</td><td>20</td><td>32</td><td>44</td><td>56</td><td>68</td><td>80</td><td>92</td></tr>\n    <tr><th>G#</th><td>9</td><td>21</td><td>33</td><td>45</td><td>57</td><td>69</td><td>81</td><td>93</td></tr>\n    <tr><th>A</th><td>10</td><td>22</td><td>34</td><td>46</td><td>58</td><td>70</td><td>82</td><td>94</td></tr>\n    <tr><th>A#</th><td>11</td><td>23</td><td>35</td><td>47</td><td>59</td><td>71</td><td>83</td><td>95</td></tr>\n    <tr><th>B</th><td>12</td><td>24</td><td>36</td><td>48</td><td>60</td><td>72</td><td>84</td><td>96</td></tr>\n</table>\n\n<p>The optional parameter <em>len</em> is the length in 1/60 seconds, the maximum\nis 255. 0 means, that the sound won't stop automatically. If the\nparameter is omitted, the current value of the voice is kept.</p>\n\n<p>By default the current sound settings of the voice are used. Add\nthe SOUND parameter to use the sound number <em>s</em> from the Sound Composer\ntool.</p>\n\n<pre class=\"example\">\nPLAY 0,49,20\nWAIT 30\nPLAY 0,53,20\nWAIT 30\nPLAY 0,56,40\nWAIT 60\n</pre>\n\n\n<h3 id=\"_STOP\" name=\"STOP\">STOP</h3>\n\n<pre>STOP [v]</pre>\n\n<p>Stops the current sound and track on voice <em>v</em>. If the parameter is\nomitted, all voices, tracks and music are stopped. If a voice's\nenvelope has a release time, the sound won't stop immediately, but\nfade out.</p>\n\n\n<h3 id=\"_VOLUME\" name=\"VOLUME\">VOLUME</h3>\n\n<pre>VOLUME v,[vol],[mix]</pre>\n\n<p>Sets the volume of voice <em>n</em> (0-3) to <em>vol</em> (0-15) and its outputs to\n<em>mix</em> (0-3):</p>\n\n<table>\n<tr><td>0</td><td>Muted</td></tr>\n<tr><td>1</td><td>Left</td></tr>\n<tr><td>2</td><td>Right</td></tr>\n<tr><td>3</td><td>Left and right (center)</td></tr>\n</table>\n\n<p>All parameters can be omitted to keep their current settings.</p>\n\n\n<h3 id=\"_SOUND\" name=\"SOUND\">SOUND</h3>\n\n<pre>SOUND v,[w],[pw],[len]</pre>\n\n<p>Sets the basic sound parameters of voice <em>v</em> (0-3).</p>\n<p><em>w</em> is the waveform:</p>\n\n<table>\n<tr><td>0</td><td>Sawtooth</td></tr>\n<tr><td>1</td><td>Triangle</td></tr>\n<tr><td>2</td><td>Pulse</td></tr>\n<tr><td>3</td><td>Noise</td></tr>\n</table>\n\n<p><em>pw</em> is the pulse width (0-15), a value of 8 results in a square\nwave. This parameter only has an effect for the pulse waveform.</p>\n<p><em>len</em> is the sound length in 1/60 seconds, the maximum is 255. 0 means,\nthat the sound won't stop automatically. If the length is set using\nthis command, the length parameter of PLAY can be omitted.</p>\n<p>All parameters can be omitted to keep their current settings.</p>\n\n\n<h3 id=\"_ENVELOPE\" name=\"ENVELOPE\">ENVELOPE</h3>\n\n<pre>ENVELOPE v,[a],[d],[s],[r]</pre>\n\n<p>Sets the volume envelope generator of voice <em>v</em> (0-3).</p>\n<p><em>a</em> is the attack time, <em>d</em> is the decay time, and <em>r</em> is the release\ntime. All times are non-linear and range from 0 (2 ms) to 15 (12 s)</p>\n<p><em>s</em> is the sustain level (0-15), which is the volume after the decay\ntime and before the sound gets released.</p>\n<p>All parameters can be omitted to keep their current settings.</p>\n\n<pre class=\"example\">\nENVELOPE 0,1,6,8,8\nPLAY 0,49,20\nWAIT 30\nPLAY 0,53,20\nWAIT 30\nPLAY 0,56,40\nWAIT 120\n</pre>\n\n\n<h3 id=\"_LFO\" name=\"LFO\">LFO</h3>\n\n<pre>LFO v,[r],[fr],[vol],[pw]</pre>\n\n<p>Sets the LFO (low frequency oscillator) of voice <em>v</em> (0-3).</p>\n<p><em>r</em> is the LFO rate and ranges from 0 (0.12 Hz) to 15 (18 Hz) in a\nnon-linear manner.</p>\n<p>The other paramters set the amount of the effect on different sound\nparameters: <em>fr</em> for frequency/pitch, <em>vol</em> for volume and <em>pw</em> for pulse\nwidth. These values range from 0 to 15.</p>\n<p>All parameters can be omitted to keep their current settings.</p>\n\n<pre class=\"example\">\nLFO 0,12,4,0,0\nPLAY 0,49,20\nWAIT 30\nPLAY 0,53,20\nWAIT 30\nPLAY 0,56,40\nWAIT 120\n</pre>\n\n\n<h3 id=\"_LFO-WAVE\" name=\"LFO WAVE,LFO,WAVE\">LFO WAVE</h3>\n\n<pre>LFO WAVE v,[w],[i],[e],[t]</pre>\n\n<p>Sets options for the LFO wave of voice <em>v</em> (0-3).\nAll parameters can be omitted to keep their current settings.</p>\n\n<table>\n<tr><td>w</td> <td>wave (0-3):<br/>\n0: triangle<br/>\n1: sawtooth<br/>\n2: square<br/>\n3: random</td></tr>\n<tr><td>i</td> <td>invert (0/1)</td></tr>\n<tr><td>e</td> <td>env mode enabled (0/1)</td></tr>\n<tr><td>t</td> <td>trigger enabled (0/1)</td></tr>\n</table>\n\n<p>By default the LFO adds its output to the normal sound parameters.\nIf invert is enabled, it subtracts.\nBy enabling the env mode, the LFO stops after one cycle, so it can\nbe used as an additional envelope generator. If the trigger is\nenabled, the LFO restarts for each played sound, otherwise it runs\ncontinuously. Trigger is enabled implicitly with the env mode.</p>\n\n\n<h3 id=\"_SOUND-SOURCE\" name=\"SOUND SOURCE,SOUND,SOURCE\">SOUND SOURCE</h3>\n\n<pre>SOUND SOURCE a</pre>\n\n<p>Sets the current data source for the PLAY, MUSIC and TRACK\ncommands to the memory address <em>a</em>. This only affects the following\ncalls to these commands, already started playback keeps its own data\nsource. The data is assumed to be in the format of the Sound Composer\ntool.</p>\n\n<p>By default ROM entry 15 is used as source.</p>\n\n\n<h3 id=\"_=MUSIC\" name=\"MUSIC\">=MUSIC</h3>\n\n<pre>MUSIC(n)</pre>\n\n<p>Returns the status of playing music. Specify <em>n</em> for the\ninformation you want to get:</p>\n\n<table>\n<tr><td>0</td><td>The current pattern</td></tr>\n<tr><td>1</td><td>The current row</td></tr>\n<tr><td>2</td><td>The current tick</td></tr>\n<tr><td>3</td><td>The current speed (0 = stopped)</td></tr>\n</table>\n\n\n<h1 id=\"Data\">Data</h1>\n\n<h3 id=\"_DATA\" name=\"DATA\">DATA</h3>\n\n<pre>DATA constant-list</pre>\n\n<p>Stores comma separated numeric and string constants (values, but\nno variables or expressions) that are accessed by the READ\ncommand. DATA commands are not executed and may be placed\nanywhere in the program.</p>\n<p>READ commands access DATA in order, from the top of a program\nuntil the bottom. All constants of all DATA commands are read as\none continuous list of items.</p>\n\n\n<h3 id=\"_READ\" name=\"READ\">READ</h3>\n\n<pre>READ var-list</pre>\n\n<p>Reads values from DATA commands and assigns them to the\ncomma separated variables in <em>var-list</em>. The program has an internal\npointer to the current DATA value. With each value read, the\npointer will move to the next DATA value.</p>\n\n<pre class=\"example\">\nFOR I=0 TO 3\n  READ TYPE$,POWER\n  PRINT TYPE$;\":\",POWER\nNEXT I\nDATA \"LASER\",10,\"BLASTER\",15\nDATA \"PLASMA\",20,\"FUSION\",30\n</pre>\n\n\n<h3 id=\"_RESTORE\" name=\"RESTORE\">RESTORE</h3>\n\n<pre>RESTORE [label]</pre>\n\n<p>Changes the internal read pointer to another position. This allows\nto reread data or to select specific data. If the label parameter\nis omitted, READ will start again from the top of the program.\nOtherwise the pointer will be set to the jump label.</p>\n\n<pre class=\"example\">\nRESTORE SHIELDS\nFOR I=0 TO 1\n  READ TYPE$,POWER\n  PRINT TYPE$;\":\",POWER\nNEXT I\nDATA \"LASER\",10,\"BLASTER\",15\nSHIELDS:\nDATA \"SIMPLE\",30,\"ADVANCED\",60\n</pre>\n\n\n<h1 id=\"Memory-Access\">Memory Access</h1>\n\n<p>LowRes NX simulates chips for graphics, sound and I/O, the cartridge\nROM, working RAM and persistent RAM. Everything is accessible in a\n64 KB memory map, which is described in the chapter\n<a href=\"#Hardware-Reference\">\"Hardware Reference\"</a>.</p>\n\n\n<h3 id=\"_=PEEK\" name=\"PEEK\">=PEEK</h3>\n\n<pre>PEEK(a)</pre>\n\n<p>Returns the byte value (0-255) at memory address <em>a</em>.</p>\n\n<pre class=\"example\">\nTOUCHSCREEN\nPRINT \"TOUCH!\"\nDO\n  IF PEEK($FF75) AND %10 THEN\n    PRINT PEEK($FF72),PEEK($FF73)\n  END IF\n  WAIT VBL\nLOOP\n</pre>\n\n<h3 id=\"_POKE\" name=\"POKE\">POKE</h3>\n\n<pre>POKE a,v</pre>\n\n<p>Sets the memory at address <em>a</em> to value <em>v</em>.\n<em>v</em> is a numeric expression from 0 to 255;\nnumeric expressions outside this range are truncated to 8 bits.</p>\n\n<pre class=\"example\">\nPOKE $9000,232\nPOKE $9002,233\n</pre>\n\n\n<h3 id=\"_=PEEKW\" name=\"PEEKW\">=PEEKW</h3>\n\n<pre>PEEKW(a)</pre>\n\n<p>Returns the two-byte value (-32768 to 32767) at memory\naddress <em>a</em>.</p>\n\n\n<h3 id=\"_POKEW\" name=\"POKEW\">POKEW</h3>\n\n<pre>POKEW a,v</pre>\n\n<p>Writes a two-byte value at memory address <em>a</em>.\n<em>v</em> is a numeric expression from -32768 to 32767;\nnumeric expressions outside this range are truncated to 16 bits.</p>\n\n\n<h3 id=\"_=PEEKL\" name=\"PEEKL\">=PEEKL</h3>\n\n<pre>PEEKL(a)</pre>\n\n<p>Returns the four-byte value (-2147483648 to 2147483647) at\nmemory address <em>a</em>.</p>\n\n\n<h3 id=\"_POKEL\" name=\"POKEL\">POKEL</h3>\n\n<pre>POKEL a,v</pre>\n\n<p>Writes a four-byte value at memory address <em>a</em>.\n<em>v</em> is a numeric expression from -2147483648 to 2147483647;\nnumeric expressions outside this range are truncated to 32 bits.</p>\n\n<pre class=\"example\">\n'EXAMPLE USES PERSISTENT RAM\nHI=PEEKL($E000)\nPRINT \"HIGHSCORE:\"\nPRINT HI\nINPUT \"SCORE:\";SC\nIF SC>HI THEN POKEL $E000,SC\nPRINT \"RESTART!\"\n</pre>\n\n\n<h3 id=\"_COPY\" name=\"COPY\">COPY</h3>\n\n<pre>COPY a,n TO d</pre>\n\n<p>Copies <em>n</em> bytes starting from memory address <em>a</em> to address <em>d</em>.\nThe source and the destination areas may overlap.</p>\n\n<pre class=\"example\">\n'COPY CHARACTERS FROM ROM\n'ENTRY 4 TO VIDEO RAM\nCOPY ROM(4),SIZE(4) TO $8000\n</pre>\n\n\n<h3 id=\"_FILL\" name=\"FILL\">FILL</h3>\n\n<pre>FILL a,n[,v]</pre>\n\n<p>Sets <em>n</em> bytes starting from memory address <em>a</em> to value <em>v</em>, or 0 if\nthe parameter is omitted.</p>\n\n\n<h3 id=\"_ROL\" name=\"ROL\">ROL</h3>\n\n<pre>ROL a,n</pre>\n\n<p>Takes the byte at address <em>a</em> and rotates its bits left by <em>n</em> places.</p>\n\n\n<h3 id=\"_ROR\" name=\"ROR\">ROR</h3>\n\n<pre>ROR a,n</pre>\n\n<p>Takes the byte at address <em>a</em> and rotates its bits right by <em>n</em> places.</p>\n\n\n<h3 id=\"_=ROM\" name=\"ROM\">=ROM</h3>\n\n<pre>ROM(n)</pre>\n\n<p>Returns the memory address of ROM entry <em>n</em>.</p>\n\n\n<h3 id=\"_=SIZE\" name=\"SIZE\">=SIZE</h3>\n\n<pre>SIZE(n)</pre>\n\n<p>Returns the number of bytes of ROM entry <em>n</em>.</p>\n\n\n<h1 id=\"Files\">Files</h1>\n\n<p>The file commands can be used to store data on a\nvirtual disk, which can contain up to 16 files. Its format is the same\nas the ROM entries part in a program file. This makes it possible to\nuse any NX program directly as a virtual disk to edit its data.</p>\n\n<p>Virtual disks are meant to be used for development tools only, for\nexample image and map editors or music programs. Games should use\npersistent memory instead. Imagine that the standard LowRes NX console\nwouldn't have a disk drive.</p>\n\n\n<h3 id=\"_LOAD\" name=\"LOAD\">LOAD</h3>\n\n<pre>LOAD f,a[,n[,o]]</pre>\n\n<p>Loads the file number <em>f</em> from the current virtual disk to\nmemory starting at address <em>a</em>.</p>\n\n<p>Optionally the parameter <em>n</em> can be used for the maximum\nnumber of bytes that should be loaded. 0 means no limit. With the\noptional parameter <em>o</em> an offset in the file can be set.</p>\n\n<p>LOAD is meant to be used for tools only. Use ROM entries for game\ndata or persistent memory for game states.</p>\n\n\n<h3 id=\"_SAVE\" name=\"SAVE\">SAVE</h3>\n\n<pre>SAVE f,c$,a,n</pre>\n\n<p>Saves <em>n</em> bytes starting at memory address <em>a</em> to the current virtual\ndisk as a file number <em>f</em> (0-15) with comment <em>c$</em> (up to 31 characters).</p>\n\n<p>If this file was loaded before, consider keeping its original comment or\nallow the user to edit it before saving. If the file is new, the comment\nshould contain at least the type of data, e.g. \"CHARACTERS\" or \"MUSIC\".</p>\n\n<p>SAVE is meant to be used for tools only. Use persistent memory to\nstore game states.</p>\n\n\n<h3 id=\"_FILES\" name=\"FILES\">FILES</h3>\n\n<pre>FILES</pre>\n\n<p>Loads the current file directory for use with FILE$.</p>\n\n\n<h3 id=\"_FILE\" name=\"FILE$\">=FILE$</h3>\n\n<pre>FILE$(f)</pre>\n\n<p>Returns the comment string of file number <em>f</em>. Call FILES before accessing the file directory to update its content, or use FILE$ directly after LOAD or SAVE.</p>\n\n<pre class=\"example\">\nFILES\nFOR I=0 TO 15\n  PRINT I,FILE$(I)\nNEXT I\n</pre>\n\n\n<h3 id=\"_=FSIZE\" name=\"FSIZE\">=FSIZE</h3>\n\n<pre>FSIZE(n)</pre>\n\n<p>Returns the number of bytes of file number <em>n</em>. Call FILES before accessing the file directory to update its content, or use FSIZE directly after LOAD or SAVE.</p>\n\n\n<h1 id=\"Math-Functions\">Math Functions</h1>\n\n<h2 id=\"Trigonometric\">Trigonometric</h2>\n\n<h3 id=\"_PI\" name=\"PI\">=PI</h3>\n\n<pre>PI</pre>\n\n<p>PI is the ratio of the circumference of a circle to its diameter:\n3.1415926535...</p>\n\n\n<h3 id=\"_SIN\" name=\"SIN\">=SIN</h3>\n\n<pre>SIN(x)</pre>\n\n<p>The sine of <em>x</em>, where <em>x</em> is in radians.</p>\n\n\n<h3 id=\"_COS\" name=\"COS\">=COS</h3>\n\n<pre>COS(x)</pre>\n\n<p>The cosine of <em>x</em>, where <em>x</em> is in radians.</p>\n\n\n<h3 id=\"_TAN\" name=\"TAN\">=TAN</h3>\n\n<pre>TAN(x)</pre>\n\n<p>The tangent of <em>x</em>, where <em>x</em> is in radians.</p>\n\n\n<h3 id=\"_ASIN\" name=\"ASIN\">=ASIN</h3>\n\n<pre>ASIN(x)</pre>\n\n<p>The arc sine of <em>x</em>, where <em>x</em> must be in the range of -1 to +1. The range of the function is <span class=\"nobr\">-(PI/2)</span> &lt;= ASIN(x) &lt;= (PI/2).</p>\n\n\n<h3 id=\"_ACOS\" name=\"ACOS\">=ACOS</h3>\n\n<pre>ACOS(x)</pre>\n\n<p>The arc cosine of <em>x</em>, where <em>x</em> must be in the range of -1 to +1. The range of the function is 0 &lt;= ACOS(x) &lt;= PI.</p>\n\n\n<h3 id=\"_ATAN\" name=\"ATAN\">=ATAN</h3>\n\n<pre>ATAN(x)</pre>\n\n<p>The arctangent of <em>x</em> in radians, i.e. the angle whose tangent is <em>x</em>.\nThe range of the function is <span class=\"nobr\">-(PI/2)</span> &lt;= ATAN(x) &lt;= (PI/2).</p>\n\n\n<h3 id=\"_HSIN\" name=\"HSIN\">=HSIN</h3>\n\n<pre>HSIN(x)</pre>\n\n<p>The hyperbolic sine of <em>x</em>.</p>\n\n\n<h3 id=\"_HCOS\" name=\"HCOS\">=HCOS</h3>\n\n<pre>HCOS(x)</pre>\n\n<p>The hyperbolic cosine of <em>x</em>.</p>\n\n\n<h3 id=\"_HTAN\" name=\"HTAN\">=HTAN</h3>\n\n<pre>HTAN(x)</pre>\n\n<p>The hyperbolic tangent of <em>x</em>.</p>\n\n\n<h2 id=\"Standard-Math\">Standard Math</h2>\n\n<h3 id=\"_ABS\" name=\"ABS\">=ABS</h3>\n\n<pre>ABS(x)</pre>\n\n<p>The absolute value of <em>x</em>.</p>\n\n\n<h3 id=\"_SGN\" name=\"SGN\">=SGN</h3>\n\n<pre>SGN(x)</pre>\n\n<p>The sign of <em>x</em>: -1 if <em>x</em> &lt; 0, 0 if <em>x</em> = 0 and +1 if <em>x</em> &gt; 0.</p>\n\n\n<h3 id=\"_INT\" name=\"INT\">=INT</h3>\n\n<pre>INT(x)</pre>\n\n<p>The largest integer not greater than <em>x</em>; e.g. INT(1.3) = 1 and\nINT(-1.3) = -2.</p>\n\n\n<h3 id=\"_EXP\" name=\"EXP\">=EXP</h3>\n\n<pre>EXP(x)</pre>\n\n<p>The exponential of <em>x</em>, i.e. the value of the base of natural\nlogarithms (e = 2,71828...) raised to the power <em>x</em>.</p>\n\n\n<h3 id=\"_LOG\" name=\"LOG\">=LOG</h3>\n\n<pre>LOG(x)</pre>\n\n<p>The natural logarithm of <em>x</em>; <em>x</em> must be greater than zero.</p>\n\n\n<h3 id=\"_SQR\" name=\"SQR\">=SQR</h3>\n\n<pre>SQR(x)</pre>\n\n<p>The non-negative square root of <em>x</em>; <em>x</em> must be non-negative.</p>\n\n\n<h2 id=\"Random-Sequences\">Random Sequences</h2>\n\n<h3 id=\"_RND\" name=\"RND\">=RND</h3>\n\n<pre>RND</pre>\n\n<p>The next number in a sequence of random numbers uniformly\ndistributed in the range 0 &lt;= RND &lt; 1.</p>\n\n<pre>RND(n)</pre>\n\n<p>The second syntax generates a random integer between 0 and <em>n</em> inclusive.</p>\n\n\n<h3 id=\"_RANDOMIZE\" name=\"RANDOMIZE,TIMER\">RANDOMIZE</h3>\n<pre>RANDOMIZE x</pre>\n\n<p>Sets the seed for random numbers to <em>x</em>, which should be an integer\nvalue. By default a program starts with seed 0, so the sequence of\nrandom numbers is always the same.</p>\n\n<pre>RANDOMIZE TIMER</pre>\n\n<p>If you want different random numbers each time you run your\nprogram, you should insert this line at the beginning.</p>\n\n<pre class=\"example\">\nRANDOMIZE TIMER\nFOR I=1 TO 16\n  PRINT RND(1000)\nNEXT I\n</pre>\n\n\n<h2 id=\"Manipulating-Numbers\">Manipulating Numbers</h2>\n\n<h3 id=\"_MIN\" name=\"MIN\">=MIN</h3>\n\n<pre>MIN(x,y)</pre>\n\n<p>The MIN function returns the smallest value of two expressions.</p>\n\n\n<h3 id=\"_MAX\" name=\"MAX\">=MAX</h3>\n\n<pre>MAX(x,y)</pre>\n\n<p>The MAX function returns the largest value of two expressions.</p>\n\n\n<h3 id=\"_SWAP\" name=\"SWAP\">SWAP</h3>\n\n<pre>SWAP var1,var2</pre>\n\n<p>Swaps the data between any two variables of the same type.</p>\n\n<pre class=\"example\">\nA=10\nB=40\nSWAP A,B\nPRINT A\nPRINT B\n</pre>\n\n\n<h3 id=\"_INC\" name=\"INC\">INC</h3>\n\n<pre>INC var</pre>\n\n<p>Increases the value of the variable by one. INC A does the same as A=A+1, but costs less CPU cycles.</p>\n\n\n<h3 id=\"_DEC\" name=\"DEC\">DEC</h3>\n\n<pre>DEC var</pre>\n\n<p>Decreases the value of the variable by one. DEC A does the same as A=A-1, but costs less CPU cycles.</p>\n\n<p></p>\n\n\n<h3 id=\"_ADD\" name=\"ADD\">ADD</h3>\n\n<pre>ADD var,x</pre>\n\n<p>Adds the value <em>x</em> to the variable, where <em>x</em> can the positive or negative. ADD A,X does the same as A=A+X, but costs less CPU cycles.</p>\n\n<pre>ADD var,x,base TO top</pre>\n\n<p>The second syntax of ADD helps with repeating counters.</p>\n\n<pre class=\"example\">\nA=0\nDO\n  ADD A,2,0 TO 10\n  PRINT A\nLOOP\n</pre>\n\n<p>It's the same as:</p>\n\n<pre class=\"example\">\nA=0\nDO\n  A=A+2\n  IF A&gt;10 THEN A=0\n  IF A&lt;0 THEN A=10\n  PRINT A\nLOOP\n</pre>\n\n<p>But again the ADD command costs less CPU cycles.</p>\n\n\n<h1 id=\"String-Functions\">String Functions</h1>\n\n<h3 id=\"_LEFT$\" name=\"LEFT$\">=LEFT$=</h3>\n\n<pre>LEFT$(s$,n)</pre>\n\n<p>Returns a new string with the first <em>n</em> characters of <em>s$</em>.</p>\n\n<pre class=\"example\">\nPRINT LEFT$(\"LOWRES NX\",3)\n</pre>\n\n<pre>LEFT$(s$,n)=a$</pre>\n\n<p>Overwrites the first characters in the variable <em>s$</em> with the first\n<em>n</em> characters of <em>a$</em>.</p>\n\n<pre class=\"example\">\nA$=\"FOORES NX\"\nLEFT$(A$,3)=\"LOWER\"\nPRINT A$\n</pre>\n\n\n<h3 id=\"_RIGHT$\" name=\"RIGHT$\">=RIGHT$=</h3>\n\n<pre>RIGHT$(s$,n)</pre>\n\n<p>Returns a new string with the last <em>n</em> characters of <em>s$</em>.</p>\n\n<pre>RIGHT$(s$,n)=a$</pre>\n\n<p>Overwrites the last characters in the variable <em>s$</em> with the last <em>n</em>\ncharacters of <em>a$</em>.</p>\n\n\n<h3 id=\"_MID$\" name=\"MID$\">=MID$=</h3>\n\n<pre>MID$(s$,p,n)</pre>\n\n<p>Returns a new string with <em>n</em> characters of <em>s$</em>, starting at\ncharacter <em>p</em>. The first character has the position 1.</p>\n\n<pre class=\"example\">\nPRINT MID$(\"LOWRES NX\",4,3)\n</pre>\n\n<pre>MID$(s$,p,n)=a$</pre>\n\n<p>Overwrites the given text range in the variable <em>s$</em> with the first\n<em>n</em> characters of <em>a$</em>.</p>\n\n<pre class=\"example\">\nA$=\"LOWFOO NX\"\nMID$(A$,4,3)=\"RESTAURANT\"\nPRINT A$\n</pre>\n\n\n<h3 id=\"_INSTR\" name=\"INSTR\">=INSTR</h3>\n\n<pre>INSTR(d$,s$[,p])</pre>\n\n<p>Searches the first occurrence of <em>s$</em> inside of <em>d$</em> and returns its\nstart position. If it's not found, the function returns 0.\nUsually the function starts searching at the beginning of the\nstring. Optionally it can start searching at position <em>p</em>.</p>\n\n<pre class=\"example\">\nPRINT INSTR(\"LOWRES NX\",\"RES\")\n</pre>\n\n\n<h3 id=\"_CHR$\" name=\"CHR$\">=CHR$</h3>\n\n<pre>CHR$(n)</pre>\n\n<p>Returns a string containing one character with ASCII code <em>n</em>.</p>\n\n<pre class=\"example\">\nFOR I=32 TO 90\n  PRINT CHR$(I)\nNEXT I\n</pre>\n\n\n<h3 id=\"_ASC\" name=\"ASC\">=ASC</h3>\n\n<pre>ASC(a$)</pre>\n\n<p>Supplies you with the ASCII code of the first character of <em>a$</em>.</p>\n\n<pre class=\"example\">\nPRINT ASC(\"L\")\n</pre>\n\n\n<h3 id=\"_LEN\" name=\"LEN\">=LEN</h3>\n\n<pre>LEN(a$)</pre>\n\n<p>Returns the number of characters in <em>a$</em>.</p>\n\n\n<h3 id=\"_VAL\" name=\"VAL\">=VAL</h3>\n\n<pre>VAL(a$)</pre>\n\n<p>Converts a number written in <em>a$</em> into a numeric value.</p>\n\n\n<h3 id=\"_STR$\" name=\"STR$\">=STR$</h3>\n\n<pre>STR$(n)</pre>\n\n<p>Converts the number <em>n</em> into a string.</p>\n\n\n<h3 id=\"_BIN$\" name=\"BIN$\">=BIN$</h3>\n\n<pre>BIN$(n[,len])</pre>\n\n<p>Converts the number <em>n</em> into a binary string with at least\n<em>len</em> digits.</p>\n\n\n<h3 id=\"_HEX$\" name=\"HEX$\">=HEX$</h3>\n\n<pre>HEX$(n[,len])</pre>\n\n<p>Converts the number <em>n</em> into a hexadecimal string with at least\n<em>len</em> digits.</p>\n\n\n<h1 id=\"System\">System</h1>\n\n<p>System commands are a link between the virtual console and the\nLowRes NX application.</p>\n\n\n<h3 id=\"_TRACE\" name=\"TRACE\">TRACE</h3>\n\n<pre>TRACE expression-list</pre>\n\n<p>Outputs text to the debugging window. Expressions can be strings or\nnumbers, separated by commas. This command is ignored if the debug\nmode is not enabled.</p>\n\n<pre class=\"example\">\n'RUN IN DEBUG MODE\nA=13\nB$=\"GO\"\nTRACE \"TEST\",A,B$\n</pre>\n\n\n<h3 id=\"_SYSTEM\" name=\"SYSTEM\">SYSTEM</h3>\n\n<pre>SYSTEM n,v</pre>\n\n<p>Sets the system status <em>n</em> to value <em>v</em>. Currently\nonly one status is available:</p>\n\n<table>\n<tr>\n<td>0</td>\n<td>Energy Saving Mode:<br/>\nEnable with <em>v</em> different from 0. In this mode the screen\nrefreshes with a very low rate whenever there is no user input.\nIt does not affect the virtual CPU, the code still runs at full speed.\n</td>\n</tr>\n</table>\n\n\n<h1 id=\"Advanced-Topics\">Advanced Topics</h1>\n\n<h2 id=\"CPU-Cycles\">CPU Cycles</h2>\n\n<p>LowRes NX has a simplified simulation of CPU cycles. There is a\nfixed limit of cycles per frame. This assures the same program\nexecution speed on all devices, so if you optimize your program on\nyour device to run smoothly, it will run the same on all other\ndevices.</p>\n\n<p>Each execution of a command, function or operator, as well as\naccess to a variable or a constant count 1 cycle. Some operations have\nadditional costs:</p>\n<ul>\n<li>String creation and modification count 1 cycle per letter.</li>\n<li>Array initialization counts 1 cycle per element.</li>\n<li>Memory area modification counts 1 cycle per byte (not single byte\nmodifications like POKE).</li>\n<li>BG area modification and text output count 2 cycles per cell (not\nsingle cell modifications like CELL).</li>\n</ul>\n\n<table>\n<tr><td>Total cycles per frame</td><td>17556</td></tr>\n<tr><td>Cycles per VBL interrupt</td><td>1140</td></tr>\n<tr><td>Cycles per raster interrupt</td><td>51</td></tr>\n</table>\n\n<p>The main program may spend any number of cycles, but when the limit\nis reached before a WAIT VBL or WAIT command, the execution continues\nin the next frame. If interrupts exceed their limit, you will see\nblack scanlines on the screen.</p>\n\n\n<h2 id=\"Hardware-Reference\">Hardware Reference</h2>\n\n<h3 id=\"Memory-Map\">Memory Map</h3>\n\n<pre>\n$0000 - Cartridge ROM (32 KB)\n\n$8000 - Character Data (4 KB)\n$9000 - BG0 Data (2 KB)\n$9800 - BG1 Data (2 KB)\n\n$A000 - Working RAM (16 KB)\n\n$E000 - Persistent RAM (4 KB)\n\n$FE00 - Sprite Registers (256 B)\n$FF00 - Color Registers (32 B)\n$FF20 - Video Registers\n$FF40 - Audio Registers\n$FF70 - I/O Registers\n</pre>\n\n<h3 id=\"Character-Data\">Character Data</h3>\n\n<p>A character is an 8x8-pixel image with 2 bits per pixel, with a\nresulting size of 16 bytes. The video RAM has space for 256\ncharacters.</p>\n\n<p>The first 8 bytes of a character contain the low bits of all its\npixels, followed by 8 more bytes containing the high bits of all\npixels.</p>\n\n<h3 id=\"BG-Data\">BG Data</h3>\n\n<p>A background is a map of 32x32 character cells. Each cell occupies two\nbytes:</p>\n<pre>\n- Character number\n- Attributes:\n    Bit  Purpose\n    0-2  Palette number\n    3    Flip X\n    4    Flip Y\n    5    Priority\n    6-7  Unused\n</pre>\n\n<h3 id=\"Persistent-RAM\">Persistent RAM</h3>\n\n<p>Imagine it as a battery buffered RAM on the game cartridge. Use it\nfor data like game positions or high score tables. The content of the\npersistent RAM will be saved automatically when you exit the program\nand loaded when you run it. Each program saves its persistent RAM\nseparately.</p>\n\n<h3 id=\"Sprite-Registers\">Sprite Registers</h3>\n\n<p>There are 64 sprites available, each occupies 4 bytes:</p>\n<pre>\n- X position\n- Y position\n- Character number\n- Attributes:\n    Bit  Purpose\n    0-2  Palette number\n    3    Flip X\n    4    Flip Y\n    5    Priority\n    6-7  Size:\n         0: 1 character (8x8 px)\n         1: 2x2 characters (16x16 px)\n         2: 3x3 characters (24x24 px)\n         3: 4x4 characters (32x32 px)\n</pre>\n<p>Note: X and Y sprite position registers have an offset of 32,\nso they can move out of the top/left screen borders without using\nnegative numbers. Using the BASIC commands, this offset is\nremoved for convenience.</p>\n\n<h3 id=\"Color-Registers\">Color Registers</h3>\n\n<p>There are 8 palettes of each 4 colors. One color is one byte:</p>\n<pre>\nBits  Component\n0-1   Blue\n2-3   Green\n4-5   Red\n</pre>\n\n<h3 id=\"Video-Registers\">Video Registers</h3>\n\n<pre>\n$FF20 - Attributes:\n    Bit  Purpose\n    0    Sprites enabled\n    1    BG0 enabled\n    2    BG1 enabled\n    3    BG0 cell size,\n    4    BG1 cell size:\n         0: 1 character (8x8 px)\n            (BG 256x256 px)\n         1: 2x2 characters (16x16 px)\n            (BG 512x512 px)\n\n$FF21 - BG0 scroll offset X\n$FF22 - BG0 scroll offset Y\n$FF23 - BG1 scroll offset X\n$FF24 - BG1 scroll offset Y\n$FF25 - Scroll offset MSB\n    (most significant bits)\n    used for big cell size only:\n    Bit  Purpose\n    0    BG0 X+256\n    1    BG0 Y+256\n    2    BG1 X+256\n    3    BG1 Y+256\n\n$FF26 - Raster line\n</pre>\n\n<h3 id=\"Audio-Registers\">Audio Registers</h3>\n\n<p>There are registers for 4 voices:</p>\n<pre>\n$FF40 - Voice 0\n$FF4C - Voice 1\n$FF58 - Voice 2\n$FF64 - Voice 3\n</pre>\n\n<p>Each voice occupies 12 bytes:</p>\n<pre>\n- Frequency low-byte\n- Frequency high-byte\n- Status:\n    Bit  Purpose\n    0-3  Volume\n    4    Mix to left\n    5    Mix to right\n    6    Init\n    7    Gate\n- Peak meter (read only)\n- Attributes:\n    Bit  Purpose\n    0-3  Pulse width\n    4-5  Wave:\n         0: Sawtooth\n         1: Triangle\n         2: Pulse\n         3: Noise\n    6    Timeout enabled\n- Length (timeout)\n- Envelope byte 1:\n    Bit  Purpose\n    0-3  Attack\n    4-7  Decay\n- Envelope byte 2:\n    Bit  Purpose\n    0-3  Sustain\n    4-7  Release\n- LFO attributes:\n    Bit  Purpose\n    0-1  Wave:\n         0: Triangle\n         1: Sawtooth\n         2: Square\n         3: Random\n    2    Invert\n    3    Env mode enabled\n    4    Trigger enabled\n- LFO settings byte 1:\n    Bit  Purpose\n    0-3  LFO Rate\n    4-7  Frequency amount\n- LFO settings byte 2:\n    Bit  Purpose\n    0-3  Volume amount\n    4-7  Pulse width amount\n- Reserved\n</pre>\n<p>Note: The frequency is a 16-bit value: f = hertz * 16</p>\n\n\n<h3 id=\"I-O-Registers\">I/O Registers</h3>\n\n<pre>\n$FF70 - Gamepad 0 status\n$FF71 - Gamepad 1 status\n\nGamepad status:\n    Bit  Purpose\n    0    Up\n    1    Down\n    2    Left\n    3    Right\n    4    Button A\n    5    Button B\n\n$FF72 - Last touch X position\n$FF73 - Last touch Y position\n$FF74 - Last pressed key (ASCII code)\n$FF75 - Status:\n    Bit  Purpose\n    0    Pause button\n    1    Touch\n\n$FF76 - Attributes:\n    Bit  Purpose\n    0-1  Gamepads enabled:\n         0: off\n         1: 1 player\n         2: 2 players\n    2    Keyboard enabled\n    3    Touchscreen enabled\n</pre>\n\n\n<h2 id=\"Sound-Data-Format\">Sound Data Format</h2>\n\n<p>This is the format used for the PLAY, MUSIC and TRACK commands.\nIt's valid to store only the sound presets, if no MUSIC or TRACK\ncommands are used with this data. If any tracks are available, all\npatterns must be stored. Empty tracks after the last used one don't\nneed to be stored.</p> \n\n<pre>\nOffset - Content\n0      - 16 sound presets\n128    - 64 patterns\n384    - 64 tracks\n</pre>\n\n<p>Each sound preset occupies 8 bytes and matches the format of the\naudio registers of one voice, but without the first 4 bytes.</p>\n\n<p>Each pattern occupies 4 bytes:</p>\n<pre>\n- Voice 0:\n    Bit  Purpose\n    0-6  Track index\n         (64 = voice unused)\n    7    Flag loop start\n- Voice 1:\n    Bit  Purpose\n    0-6  Track index\n         (64 = voice unused)\n    7    Flag loop end\n- Voice 2:\n    Bit  Purpose\n    0-6  Track index\n         (64 = voice unused)\n    7    Flag song stop\n- Voice 3:\n    Bit  Purpose\n    0-6  Track index\n         (64 = voice unused)\n</pre>\n\n<p>Each track occupies 96 bytes and consists of 32 entries with each 3\nbytes:</p>\n<pre>\n- Note pitch (0 = empty)\n- Data (ignored if note is 0):\n    Bit  Purpose\n    0-3  Volume\n    4-7  Sound\n- Control:\n    Bit  Purpose\n    0-3  Parameter\n    4-7  Command\n</pre>\n\n\n<h2 id=\"Obsolete-Syntaxes\" name=\"SPRITE.A,ATTR,DISPLAY,LFO.A\">Obsolete Syntaxes</h2>\n\n<p>These commands, functions and syntaxes should not be used anymore\nand may be removed in future versions.</p>\n\n<pre>SPRITE.A n,(pal,fx,fy,pri,s)</pre>\n\n<p>Use <a href=\"#_SPRITE\">SPRITE</a> (new syntax) or <a href=\"#_SPRITE.A\">SPRITE.A</a>\n(single value only) instead.</p>\n\n<pre>ATTR (pal,fx,fy,pri,s)[,m]</pre>\n<pre>ATTR a,m</pre>\n\n<p>Use <a href=\"#_PAL\">PAL</a>, <a href=\"#_FLIP\">FLIP</a>, <a href=\"#_PRIO\">PRIO</a> or <a href=\"#_ATTR\">ATTR</a> (single value only) instead.\nUse <a href=\"#_TINT\">TINT</a> and <a href=\"#_BG-TINT\">BG TINT</a> for replacing attribute masks.</p>\n\n<pre>DISPLAY (s,b0,b1,c0,c1)</pre>\n<pre>DISPLAY a</pre>\n\n<p>Use <a href=\"#_BG-VIEW\">BG VIEW ON/OFF</a>, <a href=\"#_SPRITE-VIEW\">SPRITE VIEW ON/OFF</a> and <a href=\"#_CELL-SIZE\">CELL SIZE</a> instead.</p>\n\n<pre>DISPLAY</pre>\n\n<p>Use <a href=\"#_=PEEK\">PEEK($FF20)</a> instead.</p>\n\n<pre>LFO.A v,(w,r,e,t)</pre>\n\n<p>Use <a href=\"#_LFO-WAVE\">LFO WAVE</a> instead.</p>\n\n\n<h2 id=\"Reserved-Keywords\">Reserved Keywords</h2>\n\n<p>The following is a list of reserved words used in LowRes NX BASIC.\nIf you use these words as variable names, a syntax error will be\ngenerated.</p>\n\n<p>Keywords marked with an asterisk have no function yet, but\nare reserved for future versions.</p>\n\n<p>\nABS,\nACOS,\nADD,\nAND,\nANIM*,\nASC,\nASIN,\nATAN,\nATTR,\nBG,\nBIN$,\nBUTTON,\nCALL,\nCELL,\nCELL.A,\nCELL.C,\nCHAR,\nCHR$,\nCLOSE*,\nCLS,\nCLW,\nCOLOR,\nCOPY,\nCOS,\nDATA,\nDEC,\nDECLARE*,\nDEF*,\nDIM,\nDISPLAY,\nDO,\nDOWN,\nELSE,\nEND,\nENVELOPE,\nEXIT,\nEXP,\nFILE$,\nFILES,\nFILL,\nFLASH*,\nFLIP,\nFN*,\nFONT,\nFOR,\nFSIZE,\nFUNCTION*,\nGAMEPAD,\nGLOBAL,\nGOSUB,\nGOTO,\nHCOS,\nHEX$,\nHIT,\nHSIN,\nHTAN,\nIF,\nINC,\nINKEY$,\nINPUT,\nINSTR,\nINT,\nKEYBOARD,\nLBOUND*,\nLEFT$,\nLEFT,\nLEN,\nLET,\nLFO,\nLFO.A,\nLOAD,\nLOCATE,\nLOG,\nLOOP,\nMAX,\nMCELL,\nMCELL.A,\nMCELL.C,\nMID$,\nMIN,\nMOD,\nMUSIC,\nNEXT,\nNOT,\nNUMBER,\nOFF,\nON,\nOPEN*,\nOPTIONAL,\nOR,\nOUTPUT*,\nPAL,\nPALETTE,\nPAUSE,\nPEEK,\nPEEKL,\nPEEKW,\nPI,\nPLAY,\nPOKE,\nPOKEL,\nPOKEW,\nPRINT,\nPRIO,\nRANDOMIZE,\nRASTER,\nREAD,\nREM,\nREPEAT,\nRESTORE,\nRETURN,\nRIGHT$,\nRIGHT,\nRND,\nROL,\nROM,\nROR,\nSAVE,\nSCROLL,\nSCROLL.X,\nSCROLL.Y,\nSGN,\nSHARED*,\nSIN,\nSIZE,\nSOUND,\nSOURCE,\nSPRITE,\nSPRITE.A,\nSPRITE.C,\nSPRITE.X,\nSPRITE.Y,\nSQR,\nSTATIC*,\nSTEP,\nSTOP,\nSTR$,\nSUB,\nSWAP,\nSYSTEM,\nTAN,\nTAP,\nTEMPO*,\nTEXT,\nTHEN,\nTIMER,\nTINT,\nTO,\nTOUCH,\nTOUCH.X,\nTOUCH.Y,\nTOUCHSCREEN,\nTRACE,\nTRACK,\nUBOUND,\nUNTIL,\nUP,\nVAL,\nVBL,\nVIEW,\nVOICE*,\nVOLUME,\nWAIT,\nWAVE,\nWEND,\nWHILE,\nWINDOW,\nWRITE*,\nXOR\n</p>\n\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "docs/readme.txt",
    "content": "***************\n* First Steps *\n***************\n\nPlease read the introduction (at least \"Getting Started\") of the manual.\n\n\n************\n* Controls *\n************\n\n- Use any real gamepad or the keyboard:\n\n\tButton  Player 1    Player 2\n\t-------+-----------+--------\n\tUP      Arrow Up    E\n\tDOWN    Arrow Down  D\n\tLEFT    Arrow Left  S\n\tRIGHT   Arrow Right F\n\tA       Z/N         Q/Tab\n\tB       X/M         A/Shift\n\n- More keys:\n\tDev Menu     Esc\n\tPause        Return/P\n\tFullscreen   Ctrl+f\n\tZoom Mode    Ctrl+z\n\tScreenshot\n\t   large     Ctrl+s\n\t   original  Ctrl+Shift+s\n\tDebug        Ctrl+d\n\tReload/Run   Ctrl+r\n\tEject        Ctrl+e\n\tVolume up    Ctrl+Plus\n\tVolume down  Ctrl+Minus\n\tQuit         Esc (if disabledev)\n\n\n************\n* Settings *\n************\n\n- Settings file:\n\tA default settings file is created on application start, if none\n\texists yet. Available options are the same as for command line\n\targuments, but each one is written in its own line and without\n\tthe leading \"-\" character.\n\tmacOS: /Users/YourName/Library/Application Support/Inutilis Software/LowRes NX/settings.txt\n\tWindows: C:\\Users\\YourName\\AppData\\Roaming\\Inutilis Software\\LowRes NX\\settings.txt\n\n- Command line arguments:\n\tThese override the options from the settings file.\n\n\t\"LowRes NX\" [-option value] [program.nx]\n\n\t-fullscreen yes/no\n\tStart the application in fullscreen mode\n\n\t-zoom 0-3\n\tStart the application in zoom mode: 0 = pixel perfect, 1 = large, 2 = overscan, 3 = squeeze.\n\n\t-disabledev yes/no\n\tDisable the Development Menu, Esc key quits LowRes NX\n\n\t-mapping 0-1\n\tSet the key mapping. 0 is standard, 1 is GameShell.\n\n\t-disabledelay yes/no\n\tDisable the delay for too short frames.\n\n\tprogram.nx\n\tName of the program to run\n\n\n*********\n* Notes *\n*********\n\n- Share programs and discuss on: https://lowresnx.inutilis.com\n\n- Development news on Twitter: @timo_inutilis\n\n- Tweet with #LowResNX.\n\n- To-do and bug list on:\n  https://github.com/timoinutilis/lowres-nx/issues\n\n- Write to timo@inutilis.com.\n"
  },
  {
    "path": "extras/LowRes NX.sublime-syntax",
    "content": "%YAML 1.2\n---\n# See http://www.sublimetext.com/docs/3/syntax.html\nfile_extensions:\n  - nx\nname: LowRes NX\nscope: source.lowres-nx\ncontexts:\n  main:\n    # Strings\n    - match: '\"'\n      scope: punctuation.definition.string.begin.lowres-nx\n      push: double_quoted_string\n\n    # Comments begin with 'REM', # or ' and finish at the end of the line\n    - match: '^\\s*(REM|#|'')'\n      scope: punctuation.definition.comment.lowres-nx\n      push: line_comment\n\n    # Keywords\n    - match: '\\b(ABS|ACOS|ADD|ASC|ASIN|ATAN|ATTR|BG|BIN|BUTTON|CALL|CELL\\.A|CELL\\.C|CELL|CHAR|CHR|CLS|CLW|COLOR|COPY|COS|DATA|DEC|DIM|DISPLAY|DOWN|DO|ELSE|END|ENVELOPE|EXIT|EXP|FILE|FILES|FILL|FONT|FOR|FSIZE|GAMEPAD|GLOBAL|GOSUB|GOTO|HEX|HCOS|HIT|HSIN|HTAN|IF|INC|INKEY|INPUT|INSTR|INT|KEYBOARD|LEFT|LEN|LET|LFO\\.A|LFO|LOAD|LOCATE|LOG|LOOP|MAX|MID|MIN|NEXT|NUMBER|OFF|ON|PALETTE|PAUSE|PEEK|PEEKL|PEEKW|PI|PLAY|POKE|POKEL|POKEW|PRINT|RANDOMIZE|RASTER|READ|REPEAT|RESTORE|RETURN|RIGHT|RND|ROL|ROM|ROR|SAVE|SCROLL\\.X|SCROLL\\.Y|SCROLL|SGN|SIN|SIZE|SOUND|SOURCE|SPRITE\\.A|SPRITE\\.C|SPRITE\\.X|SPRITE\\.Y|SPRITE|SQR|STEP|STOP|STR|SUB|SWAP|SYSTEM|TAN|TAP|TEXT|THEN|TIMER|TO|TOUCH\\.X|TOUCH\\.Y|TOUCH|TRACE|UNTIL|UP|VAL|VBL|VOLUME|WAIT|WEND|WHILE|WINDOW|PAL|FLIP|PRIO|VIEW|WAVE)\\b'\n      scope: keyword.control.lowres-nx\n\n    # Numbers\n    - match: '\\b(-)?[0-9.]+\\b'\n      scope: constant.numeric.lowres-nx\n\n    # Operators\n    - match: '\\b(AND|OR|XOR|NOT|MOD)\\b'\n      scope: keyword.operator.word.lowres-nx\n    - match: <\\=|>\\=|\\=|<|>|<>\n      scope: keyword.operator.comparison.lowres-nx\n    - match: \\+|\\-|\\*|/|\\\\\n      scope: keyword.operator.arithmetic.lowres-nx\n\n    # Punctuation\n    - match: ','\n      scope: punctuation.separator.lowres-nx\n\n  double_quoted_string:\n    - meta_scope: string.quoted.double.lowres-nx\n    - match: '\\\\.'\n      scope: constant.character.escape.lowres-nx\n    - match: '\"'\n      scope: punctuation.definition.string.end.lowres-nx\n      pop: true\n\n  line_comment:\n    - meta_scope: comment.line.lowres-nx\n    - match: $\n      pop: true\n"
  },
  {
    "path": "libretro/libretro.h",
    "content": "/* Copyright (C) 2010-2016 The RetroArch team\n *\n * ---------------------------------------------------------------------------------------\n * The following license statement only applies to this libretro API header (libretro.h).\n * ---------------------------------------------------------------------------------------\n *\n * Permission is hereby granted, free of charge,\n * to any person obtaining a copy of this software and associated documentation files (the \"Software\"),\n * to deal in the Software without restriction, including without limitation the rights to\n * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,\n * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n\n#ifndef LIBRETRO_H__\n#define LIBRETRO_H__\n\n#include <stdint.h>\n#include <stddef.h>\n#include <limits.h>\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef __cplusplus\n#if defined(_MSC_VER) && !defined(SN_TARGET_PS3)\n/* Hack applied for MSVC when compiling in C89 mode\n * as it isn't C99-compliant. */\n#define bool unsigned char\n#define true 1\n#define false 0\n#else\n#include <stdbool.h>\n#endif\n#endif\n\n#ifndef RETRO_CALLCONV\n#  if defined(__GNUC__) && defined(__i386__) && !defined(__x86_64__)\n#    define RETRO_CALLCONV __attribute__((cdecl))\n#  elif defined(_MSC_VER) && defined(_M_X86) && !defined(_M_X64)\n#    define RETRO_CALLCONV __cdecl\n#  else\n#    define RETRO_CALLCONV /* all other platforms only have one calling convention each */\n#  endif\n#endif\n\n#ifndef RETRO_API\n#  if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)\n#    ifdef RETRO_IMPORT_SYMBOLS\n#      ifdef __GNUC__\n#        define RETRO_API RETRO_CALLCONV __attribute__((__dllimport__))\n#      else\n#        define RETRO_API RETRO_CALLCONV __declspec(dllimport)\n#      endif\n#    else\n#      ifdef __GNUC__\n#        define RETRO_API RETRO_CALLCONV __attribute__((__dllexport__))\n#      else\n#        define RETRO_API RETRO_CALLCONV __declspec(dllexport)\n#      endif\n#    endif\n#  else\n#      if defined(__GNUC__) && __GNUC__ >= 4 && !defined(__CELLOS_LV2__)\n#        define RETRO_API RETRO_CALLCONV __attribute__((__visibility__(\"default\")))\n#      else\n#        define RETRO_API RETRO_CALLCONV\n#      endif\n#  endif\n#endif\n\n/* Used for checking API/ABI mismatches that can break libretro \n * implementations.\n * It is not incremented for compatible changes to the API.\n */\n#define RETRO_API_VERSION         1\n\n/*\n * Libretro's fundamental device abstractions.\n *\n * Libretro's input system consists of some standardized device types,\n * such as a joypad (with/without analog), mouse, keyboard, lightgun \n * and a pointer.\n *\n * The functionality of these devices are fixed, and individual cores \n * map their own concept of a controller to libretro's abstractions.\n * This makes it possible for frontends to map the abstract types to a \n * real input device, and not having to worry about binding input \n * correctly to arbitrary controller layouts.\n */\n\n#define RETRO_DEVICE_TYPE_SHIFT         8\n#define RETRO_DEVICE_MASK               ((1 << RETRO_DEVICE_TYPE_SHIFT) - 1)\n#define RETRO_DEVICE_SUBCLASS(base, id) (((id + 1) << RETRO_DEVICE_TYPE_SHIFT) | base)\n\n/* Input disabled. */\n#define RETRO_DEVICE_NONE         0\n\n/* The JOYPAD is called RetroPad. It is essentially a Super Nintendo \n * controller, but with additional L2/R2/L3/R3 buttons, similar to a \n * PS1 DualShock. */\n#define RETRO_DEVICE_JOYPAD       1\n\n/* The mouse is a simple mouse, similar to Super Nintendo's mouse.\n * X and Y coordinates are reported relatively to last poll (poll callback).\n * It is up to the libretro implementation to keep track of where the mouse \n * pointer is supposed to be on the screen.\n * The frontend must make sure not to interfere with its own hardware \n * mouse pointer.\n */\n#define RETRO_DEVICE_MOUSE        2\n\n/* KEYBOARD device lets one poll for raw key pressed.\n * It is poll based, so input callback will return with the current \n * pressed state.\n * For event/text based keyboard input, see\n * RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK.\n */\n#define RETRO_DEVICE_KEYBOARD     3\n\n/* Lightgun X/Y coordinates are reported relatively to last poll,\n * similar to mouse. */\n#define RETRO_DEVICE_LIGHTGUN     4\n\n/* The ANALOG device is an extension to JOYPAD (RetroPad).\n * Similar to DualShock it adds two analog sticks.\n * This is treated as a separate device type as it returns values in the \n * full analog range of [-0x8000, 0x7fff]. Positive X axis is right.\n * Positive Y axis is down.\n * Only use ANALOG type when polling for analog values of the axes.\n */\n#define RETRO_DEVICE_ANALOG       5\n\n/* Abstracts the concept of a pointing mechanism, e.g. touch.\n * This allows libretro to query in absolute coordinates where on the \n * screen a mouse (or something similar) is being placed.\n * For a touch centric device, coordinates reported are the coordinates\n * of the press.\n *\n * Coordinates in X and Y are reported as:\n * [-0x7fff, 0x7fff]: -0x7fff corresponds to the far left/top of the screen,\n * and 0x7fff corresponds to the far right/bottom of the screen.\n * The \"screen\" is here defined as area that is passed to the frontend and \n * later displayed on the monitor.\n *\n * The frontend is free to scale/resize this screen as it sees fit, however,\n * (X, Y) = (-0x7fff, -0x7fff) will correspond to the top-left pixel of the \n * game image, etc.\n *\n * To check if the pointer coordinates are valid (e.g. a touch display \n * actually being touched), PRESSED returns 1 or 0.\n *\n * If using a mouse on a desktop, PRESSED will usually correspond to the \n * left mouse button, but this is a frontend decision.\n * PRESSED will only return 1 if the pointer is inside the game screen.\n *\n * For multi-touch, the index variable can be used to successively query \n * more presses.\n * If index = 0 returns true for _PRESSED, coordinates can be extracted\n * with _X, _Y for index = 0. One can then query _PRESSED, _X, _Y with \n * index = 1, and so on.\n * Eventually _PRESSED will return false for an index. No further presses \n * are registered at this point. */\n#define RETRO_DEVICE_POINTER      6\n\n/* Buttons for the RetroPad (JOYPAD).\n * The placement of these is equivalent to placements on the \n * Super Nintendo controller.\n * L2/R2/L3/R3 buttons correspond to the PS1 DualShock. */\n#define RETRO_DEVICE_ID_JOYPAD_B        0\n#define RETRO_DEVICE_ID_JOYPAD_Y        1\n#define RETRO_DEVICE_ID_JOYPAD_SELECT   2\n#define RETRO_DEVICE_ID_JOYPAD_START    3\n#define RETRO_DEVICE_ID_JOYPAD_UP       4\n#define RETRO_DEVICE_ID_JOYPAD_DOWN     5\n#define RETRO_DEVICE_ID_JOYPAD_LEFT     6\n#define RETRO_DEVICE_ID_JOYPAD_RIGHT    7\n#define RETRO_DEVICE_ID_JOYPAD_A        8\n#define RETRO_DEVICE_ID_JOYPAD_X        9\n#define RETRO_DEVICE_ID_JOYPAD_L       10\n#define RETRO_DEVICE_ID_JOYPAD_R       11\n#define RETRO_DEVICE_ID_JOYPAD_L2      12\n#define RETRO_DEVICE_ID_JOYPAD_R2      13\n#define RETRO_DEVICE_ID_JOYPAD_L3      14\n#define RETRO_DEVICE_ID_JOYPAD_R3      15\n\n/* Index / Id values for ANALOG device. */\n#define RETRO_DEVICE_INDEX_ANALOG_LEFT   0\n#define RETRO_DEVICE_INDEX_ANALOG_RIGHT  1\n#define RETRO_DEVICE_ID_ANALOG_X         0\n#define RETRO_DEVICE_ID_ANALOG_Y         1\n\n/* Id values for MOUSE. */\n#define RETRO_DEVICE_ID_MOUSE_X                0\n#define RETRO_DEVICE_ID_MOUSE_Y                1\n#define RETRO_DEVICE_ID_MOUSE_LEFT             2\n#define RETRO_DEVICE_ID_MOUSE_RIGHT            3\n#define RETRO_DEVICE_ID_MOUSE_WHEELUP          4\n#define RETRO_DEVICE_ID_MOUSE_WHEELDOWN        5\n#define RETRO_DEVICE_ID_MOUSE_MIDDLE           6\n#define RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP    7\n#define RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN  8\n\n/* Id values for LIGHTGUN types. */\n#define RETRO_DEVICE_ID_LIGHTGUN_X        0\n#define RETRO_DEVICE_ID_LIGHTGUN_Y        1\n#define RETRO_DEVICE_ID_LIGHTGUN_TRIGGER  2\n#define RETRO_DEVICE_ID_LIGHTGUN_CURSOR   3\n#define RETRO_DEVICE_ID_LIGHTGUN_TURBO    4\n#define RETRO_DEVICE_ID_LIGHTGUN_PAUSE    5\n#define RETRO_DEVICE_ID_LIGHTGUN_START    6\n\n/* Id values for POINTER. */\n#define RETRO_DEVICE_ID_POINTER_X         0\n#define RETRO_DEVICE_ID_POINTER_Y         1\n#define RETRO_DEVICE_ID_POINTER_PRESSED   2\n\n/* Returned from retro_get_region(). */\n#define RETRO_REGION_NTSC  0\n#define RETRO_REGION_PAL   1\n\n/* Id values for LANGUAGE */\nenum retro_language\n{\n   RETRO_LANGUAGE_ENGLISH             =  0,\n   RETRO_LANGUAGE_JAPANESE            =  1,\n   RETRO_LANGUAGE_FRENCH              =  2,\n   RETRO_LANGUAGE_SPANISH             =  3,\n   RETRO_LANGUAGE_GERMAN              =  4,\n   RETRO_LANGUAGE_ITALIAN             =  5,\n   RETRO_LANGUAGE_DUTCH               =  6,\n   RETRO_LANGUAGE_PORTUGUESE          =  7,\n   RETRO_LANGUAGE_RUSSIAN             =  8,\n   RETRO_LANGUAGE_KOREAN              =  9,\n   RETRO_LANGUAGE_CHINESE_TRADITIONAL = 10,\n   RETRO_LANGUAGE_CHINESE_SIMPLIFIED  = 11,\n   RETRO_LANGUAGE_ESPERANTO           = 12,\n   RETRO_LANGUAGE_POLISH              = 13,\n   RETRO_LANGUAGE_LAST,\n\n   /* Ensure sizeof(enum) == sizeof(int) */\n   RETRO_LANGUAGE_DUMMY          = INT_MAX \n};\n\n/* Passed to retro_get_memory_data/size().\n * If the memory type doesn't apply to the \n * implementation NULL/0 can be returned.\n */\n#define RETRO_MEMORY_MASK        0xff\n\n/* Regular save RAM. This RAM is usually found on a game cartridge,\n * backed up by a battery.\n * If save game data is too complex for a single memory buffer,\n * the SAVE_DIRECTORY (preferably) or SYSTEM_DIRECTORY environment\n * callback can be used. */\n#define RETRO_MEMORY_SAVE_RAM    0\n\n/* Some games have a built-in clock to keep track of time.\n * This memory is usually just a couple of bytes to keep track of time.\n */\n#define RETRO_MEMORY_RTC         1\n\n/* System ram lets a frontend peek into a game systems main RAM. */\n#define RETRO_MEMORY_SYSTEM_RAM  2\n\n/* Video ram lets a frontend peek into a game systems video RAM (VRAM). */\n#define RETRO_MEMORY_VIDEO_RAM   3\n\n/* Keysyms used for ID in input state callback when polling RETRO_KEYBOARD. */\nenum retro_key\n{\n   RETROK_UNKNOWN        = 0,\n   RETROK_FIRST          = 0,\n   RETROK_BACKSPACE      = 8,\n   RETROK_TAB            = 9,\n   RETROK_CLEAR          = 12,\n   RETROK_RETURN         = 13,\n   RETROK_PAUSE          = 19,\n   RETROK_ESCAPE         = 27,\n   RETROK_SPACE          = 32,\n   RETROK_EXCLAIM        = 33,\n   RETROK_QUOTEDBL       = 34,\n   RETROK_HASH           = 35,\n   RETROK_DOLLAR         = 36,\n   RETROK_AMPERSAND      = 38,\n   RETROK_QUOTE          = 39,\n   RETROK_LEFTPAREN      = 40,\n   RETROK_RIGHTPAREN     = 41,\n   RETROK_ASTERISK       = 42,\n   RETROK_PLUS           = 43,\n   RETROK_COMMA          = 44,\n   RETROK_MINUS          = 45,\n   RETROK_PERIOD         = 46,\n   RETROK_SLASH          = 47,\n   RETROK_0              = 48,\n   RETROK_1              = 49,\n   RETROK_2              = 50,\n   RETROK_3              = 51,\n   RETROK_4              = 52,\n   RETROK_5              = 53,\n   RETROK_6              = 54,\n   RETROK_7              = 55,\n   RETROK_8              = 56,\n   RETROK_9              = 57,\n   RETROK_COLON          = 58,\n   RETROK_SEMICOLON      = 59,\n   RETROK_LESS           = 60,\n   RETROK_EQUALS         = 61,\n   RETROK_GREATER        = 62,\n   RETROK_QUESTION       = 63,\n   RETROK_AT             = 64,\n   RETROK_LEFTBRACKET    = 91,\n   RETROK_BACKSLASH      = 92,\n   RETROK_RIGHTBRACKET   = 93,\n   RETROK_CARET          = 94,\n   RETROK_UNDERSCORE     = 95,\n   RETROK_BACKQUOTE      = 96,\n   RETROK_a              = 97,\n   RETROK_b              = 98,\n   RETROK_c              = 99,\n   RETROK_d              = 100,\n   RETROK_e              = 101,\n   RETROK_f              = 102,\n   RETROK_g              = 103,\n   RETROK_h              = 104,\n   RETROK_i              = 105,\n   RETROK_j              = 106,\n   RETROK_k              = 107,\n   RETROK_l              = 108,\n   RETROK_m              = 109,\n   RETROK_n              = 110,\n   RETROK_o              = 111,\n   RETROK_p              = 112,\n   RETROK_q              = 113,\n   RETROK_r              = 114,\n   RETROK_s              = 115,\n   RETROK_t              = 116,\n   RETROK_u              = 117,\n   RETROK_v              = 118,\n   RETROK_w              = 119,\n   RETROK_x              = 120,\n   RETROK_y              = 121,\n   RETROK_z              = 122,\n   RETROK_DELETE         = 127,\n\n   RETROK_KP0            = 256,\n   RETROK_KP1            = 257,\n   RETROK_KP2            = 258,\n   RETROK_KP3            = 259,\n   RETROK_KP4            = 260,\n   RETROK_KP5            = 261,\n   RETROK_KP6            = 262,\n   RETROK_KP7            = 263,\n   RETROK_KP8            = 264,\n   RETROK_KP9            = 265,\n   RETROK_KP_PERIOD      = 266,\n   RETROK_KP_DIVIDE      = 267,\n   RETROK_KP_MULTIPLY    = 268,\n   RETROK_KP_MINUS       = 269,\n   RETROK_KP_PLUS        = 270,\n   RETROK_KP_ENTER       = 271,\n   RETROK_KP_EQUALS      = 272,\n\n   RETROK_UP             = 273,\n   RETROK_DOWN           = 274,\n   RETROK_RIGHT          = 275,\n   RETROK_LEFT           = 276,\n   RETROK_INSERT         = 277,\n   RETROK_HOME           = 278,\n   RETROK_END            = 279,\n   RETROK_PAGEUP         = 280,\n   RETROK_PAGEDOWN       = 281,\n\n   RETROK_F1             = 282,\n   RETROK_F2             = 283,\n   RETROK_F3             = 284,\n   RETROK_F4             = 285,\n   RETROK_F5             = 286,\n   RETROK_F6             = 287,\n   RETROK_F7             = 288,\n   RETROK_F8             = 289,\n   RETROK_F9             = 290,\n   RETROK_F10            = 291,\n   RETROK_F11            = 292,\n   RETROK_F12            = 293,\n   RETROK_F13            = 294,\n   RETROK_F14            = 295,\n   RETROK_F15            = 296,\n\n   RETROK_NUMLOCK        = 300,\n   RETROK_CAPSLOCK       = 301,\n   RETROK_SCROLLOCK      = 302,\n   RETROK_RSHIFT         = 303,\n   RETROK_LSHIFT         = 304,\n   RETROK_RCTRL          = 305,\n   RETROK_LCTRL          = 306,\n   RETROK_RALT           = 307,\n   RETROK_LALT           = 308,\n   RETROK_RMETA          = 309,\n   RETROK_LMETA          = 310,\n   RETROK_LSUPER         = 311,\n   RETROK_RSUPER         = 312,\n   RETROK_MODE           = 313,\n   RETROK_COMPOSE        = 314,\n\n   RETROK_HELP           = 315,\n   RETROK_PRINT          = 316,\n   RETROK_SYSREQ         = 317,\n   RETROK_BREAK          = 318,\n   RETROK_MENU           = 319,\n   RETROK_POWER          = 320,\n   RETROK_EURO           = 321,\n   RETROK_UNDO           = 322,\n\n   RETROK_LAST,\n\n   RETROK_DUMMY          = INT_MAX /* Ensure sizeof(enum) == sizeof(int) */\n};\n\nenum retro_mod\n{\n   RETROKMOD_NONE       = 0x0000,\n\n   RETROKMOD_SHIFT      = 0x01,\n   RETROKMOD_CTRL       = 0x02,\n   RETROKMOD_ALT        = 0x04,\n   RETROKMOD_META       = 0x08,\n\n   RETROKMOD_NUMLOCK    = 0x10,\n   RETROKMOD_CAPSLOCK   = 0x20,\n   RETROKMOD_SCROLLOCK  = 0x40,\n\n   RETROKMOD_DUMMY = INT_MAX /* Ensure sizeof(enum) == sizeof(int) */\n};\n\n/* If set, this call is not part of the public libretro API yet. It can \n * change or be removed at any time. */\n#define RETRO_ENVIRONMENT_EXPERIMENTAL 0x10000\n/* Environment callback to be used internally in frontend. */\n#define RETRO_ENVIRONMENT_PRIVATE 0x20000\n\n/* Environment commands. */\n#define RETRO_ENVIRONMENT_SET_ROTATION  1  /* const unsigned * --\n                                            * Sets screen rotation of graphics.\n                                            * Is only implemented if rotation can be accelerated by hardware.\n                                            * Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180, \n                                            * 270 degrees counter-clockwise respectively.\n                                            */\n#define RETRO_ENVIRONMENT_GET_OVERSCAN  2  /* bool * --\n                                            * Boolean value whether or not the implementation should use overscan, \n                                            * or crop away overscan.\n                                            */\n#define RETRO_ENVIRONMENT_GET_CAN_DUPE  3  /* bool * --\n                                            * Boolean value whether or not frontend supports frame duping,\n                                            * passing NULL to video frame callback.\n                                            */\n\n                                           /* Environ 4, 5 are no longer supported (GET_VARIABLE / SET_VARIABLES), \n                                            * and reserved to avoid possible ABI clash.\n                                            */\n\n#define RETRO_ENVIRONMENT_SET_MESSAGE   6  /* const struct retro_message * --\n                                            * Sets a message to be displayed in implementation-specific manner \n                                            * for a certain amount of 'frames'.\n                                            * Should not be used for trivial messages, which should simply be \n                                            * logged via RETRO_ENVIRONMENT_GET_LOG_INTERFACE (or as a \n                                            * fallback, stderr).\n                                            */\n#define RETRO_ENVIRONMENT_SHUTDOWN      7  /* N/A (NULL) --\n                                            * Requests the frontend to shutdown.\n                                            * Should only be used if game has a specific\n                                            * way to shutdown the game from a menu item or similar.\n                                            */\n#define RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL 8\n                                           /* const unsigned * --\n                                            * Gives a hint to the frontend how demanding this implementation\n                                            * is on a system. E.g. reporting a level of 2 means\n                                            * this implementation should run decently on all frontends\n                                            * of level 2 and up.\n                                            *\n                                            * It can be used by the frontend to potentially warn\n                                            * about too demanding implementations.\n                                            *\n                                            * The levels are \"floating\".\n                                            *\n                                            * This function can be called on a per-game basis,\n                                            * as certain games an implementation can play might be\n                                            * particularly demanding.\n                                            * If called, it should be called in retro_load_game().\n                                            */\n#define RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY 9\n                                           /* const char ** --\n                                            * Returns the \"system\" directory of the frontend.\n                                            * This directory can be used to store system specific \n                                            * content such as BIOSes, configuration data, etc.\n                                            * The returned value can be NULL.\n                                            * If so, no such directory is defined,\n                                            * and it's up to the implementation to find a suitable directory.\n                                            *\n                                            * NOTE: Some cores used this folder also for \"save\" data such as \n                                            * memory cards, etc, for lack of a better place to put it.\n                                            * This is now discouraged, and if possible, cores should try to \n                                            * use the new GET_SAVE_DIRECTORY.\n                                            */\n#define RETRO_ENVIRONMENT_SET_PIXEL_FORMAT 10\n                                           /* const enum retro_pixel_format * --\n                                            * Sets the internal pixel format used by the implementation.\n                                            * The default pixel format is RETRO_PIXEL_FORMAT_0RGB1555.\n                                            * This pixel format however, is deprecated (see enum retro_pixel_format).\n                                            * If the call returns false, the frontend does not support this pixel \n                                            * format.\n                                            *\n                                            * This function should be called inside retro_load_game() or \n                                            * retro_get_system_av_info().\n                                            */\n#define RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS 11\n                                           /* const struct retro_input_descriptor * --\n                                            * Sets an array of retro_input_descriptors.\n                                            * It is up to the frontend to present this in a usable way.\n                                            * The array is terminated by retro_input_descriptor::description \n                                            * being set to NULL.\n                                            * This function can be called at any time, but it is recommended \n                                            * to call it as early as possible.\n                                            */\n#define RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK 12\n                                           /* const struct retro_keyboard_callback * --\n                                            * Sets a callback function used to notify core about keyboard events.\n                                            */\n#define RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE 13\n                                           /* const struct retro_disk_control_callback * --\n                                            * Sets an interface which frontend can use to eject and insert \n                                            * disk images.\n                                            * This is used for games which consist of multiple images and \n                                            * must be manually swapped out by the user (e.g. PSX).\n                                            */\n#define RETRO_ENVIRONMENT_SET_HW_RENDER 14\n                                           /* struct retro_hw_render_callback * --\n                                            * Sets an interface to let a libretro core render with \n                                            * hardware acceleration.\n                                            * Should be called in retro_load_game().\n                                            * If successful, libretro cores will be able to render to a \n                                            * frontend-provided framebuffer.\n                                            * The size of this framebuffer will be at least as large as \n                                            * max_width/max_height provided in get_av_info().\n                                            * If HW rendering is used, pass only RETRO_HW_FRAME_BUFFER_VALID or \n                                            * NULL to retro_video_refresh_t.\n                                            */\n#define RETRO_ENVIRONMENT_GET_VARIABLE 15\n                                           /* struct retro_variable * --\n                                            * Interface to acquire user-defined information from environment\n                                            * that cannot feasibly be supported in a multi-system way.\n                                            * 'key' should be set to a key which has already been set by \n                                            * SET_VARIABLES.\n                                            * 'data' will be set to a value or NULL.\n                                            */\n#define RETRO_ENVIRONMENT_SET_VARIABLES 16\n                                           /* const struct retro_variable * --\n                                            * Allows an implementation to signal the environment\n                                            * which variables it might want to check for later using \n                                            * GET_VARIABLE.\n                                            * This allows the frontend to present these variables to \n                                            * a user dynamically.\n                                            * This should be called as early as possible (ideally in \n                                            * retro_set_environment).\n                                            *\n                                            * 'data' points to an array of retro_variable structs \n                                            * terminated by a { NULL, NULL } element.\n                                            * retro_variable::key should be namespaced to not collide \n                                            * with other implementations' keys. E.g. A core called \n                                            * 'foo' should use keys named as 'foo_option'.\n                                            * retro_variable::value should contain a human readable \n                                            * description of the key as well as a '|' delimited list \n                                            * of expected values.\n                                            *\n                                            * The number of possible options should be very limited, \n                                            * i.e. it should be feasible to cycle through options \n                                            * without a keyboard.\n                                            *\n                                            * First entry should be treated as a default.\n                                            *\n                                            * Example entry:\n                                            * { \"foo_option\", \"Speed hack coprocessor X; false|true\" }\n                                            *\n                                            * Text before first ';' is description. This ';' must be \n                                            * followed by a space, and followed by a list of possible \n                                            * values split up with '|'.\n                                            *\n                                            * Only strings are operated on. The possible values will \n                                            * generally be displayed and stored as-is by the frontend.\n                                            */\n#define RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE 17\n                                           /* bool * --\n                                            * Result is set to true if some variables are updated by\n                                            * frontend since last call to RETRO_ENVIRONMENT_GET_VARIABLE.\n                                            * Variables should be queried with GET_VARIABLE.\n                                            */\n#define RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME 18\n                                           /* const bool * --\n                                            * If true, the libretro implementation supports calls to \n                                            * retro_load_game() with NULL as argument.\n                                            * Used by cores which can run without particular game data.\n                                            * This should be called within retro_set_environment() only.\n                                            */\n#define RETRO_ENVIRONMENT_GET_LIBRETRO_PATH 19\n                                           /* const char ** --\n                                            * Retrieves the absolute path from where this libretro \n                                            * implementation was loaded.\n                                            * NULL is returned if the libretro was loaded statically \n                                            * (i.e. linked statically to frontend), or if the path cannot be \n                                            * determined.\n                                            * Mostly useful in cooperation with SET_SUPPORT_NO_GAME as assets can \n                                            * be loaded without ugly hacks.\n                                            */\n                                           \n                                           /* Environment 20 was an obsolete version of SET_AUDIO_CALLBACK. \n                                            * It was not used by any known core at the time,\n                                            * and was removed from the API. */\n#define RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK 22\n                                           /* const struct retro_audio_callback * --\n                                            * Sets an interface which is used to notify a libretro core about audio \n                                            * being available for writing.\n                                            * The callback can be called from any thread, so a core using this must \n                                            * have a thread safe audio implementation.\n                                            * It is intended for games where audio and video are completely \n                                            * asynchronous and audio can be generated on the fly.\n                                            * This interface is not recommended for use with emulators which have \n                                            * highly synchronous audio.\n                                            *\n                                            * The callback only notifies about writability; the libretro core still \n                                            * has to call the normal audio callbacks\n                                            * to write audio. The audio callbacks must be called from within the \n                                            * notification callback.\n                                            * The amount of audio data to write is up to the implementation.\n                                            * Generally, the audio callback will be called continously in a loop.\n                                            *\n                                            * Due to thread safety guarantees and lack of sync between audio and \n                                            * video, a frontend  can selectively disallow this interface based on \n                                            * internal configuration. A core using this interface must also \n                                            * implement the \"normal\" audio interface.\n                                            *\n                                            * A libretro core using SET_AUDIO_CALLBACK should also make use of \n                                            * SET_FRAME_TIME_CALLBACK.\n                                            */\n#define RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK 21\n                                           /* const struct retro_frame_time_callback * --\n                                            * Lets the core know how much time has passed since last \n                                            * invocation of retro_run().\n                                            * The frontend can tamper with the timing to fake fast-forward, \n                                            * slow-motion, frame stepping, etc.\n                                            * In this case the delta time will use the reference value \n                                            * in frame_time_callback..\n                                            */\n#define RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE 23\n                                           /* struct retro_rumble_interface * --\n                                            * Gets an interface which is used by a libretro core to set \n                                            * state of rumble motors in controllers.\n                                            * A strong and weak motor is supported, and they can be \n                                            * controlled indepedently.\n                                            */\n#define RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES 24\n                                           /* uint64_t * --\n                                            * Gets a bitmask telling which device type are expected to be \n                                            * handled properly in a call to retro_input_state_t.\n                                            * Devices which are not handled or recognized always return \n                                            * 0 in retro_input_state_t.\n                                            * Example bitmask: caps = (1 << RETRO_DEVICE_JOYPAD) | (1 << RETRO_DEVICE_ANALOG).\n                                            * Should only be called in retro_run().\n                                            */\n#define RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE (25 | RETRO_ENVIRONMENT_EXPERIMENTAL)\n                                           /* struct retro_sensor_interface * --\n                                            * Gets access to the sensor interface.\n                                            * The purpose of this interface is to allow\n                                            * setting state related to sensors such as polling rate, \n                                            * enabling/disable it entirely, etc.\n                                            * Reading sensor state is done via the normal \n                                            * input_state_callback API.\n                                            */\n#define RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE (26 | RETRO_ENVIRONMENT_EXPERIMENTAL)\n                                           /* struct retro_camera_callback * --\n                                            * Gets an interface to a video camera driver.\n                                            * A libretro core can use this interface to get access to a \n                                            * video camera.\n                                            * New video frames are delivered in a callback in same \n                                            * thread as retro_run().\n                                            *\n                                            * GET_CAMERA_INTERFACE should be called in retro_load_game().\n                                            *\n                                            * Depending on the camera implementation used, camera frames \n                                            * will be delivered as a raw framebuffer,\n                                            * or as an OpenGL texture directly.\n                                            *\n                                            * The core has to tell the frontend here which types of \n                                            * buffers can be handled properly.\n                                            * An OpenGL texture can only be handled when using a \n                                            * libretro GL core (SET_HW_RENDER).\n                                            * It is recommended to use a libretro GL core when \n                                            * using camera interface.\n                                            *\n                                            * The camera is not started automatically. The retrieved start/stop \n                                            * functions must be used to explicitly\n                                            * start and stop the camera driver.\n                                            */\n#define RETRO_ENVIRONMENT_GET_LOG_INTERFACE 27\n                                           /* struct retro_log_callback * --\n                                            * Gets an interface for logging. This is useful for \n                                            * logging in a cross-platform way\n                                            * as certain platforms cannot use use stderr for logging. \n                                            * It also allows the frontend to\n                                            * show logging information in a more suitable way.\n                                            * If this interface is not used, libretro cores should \n                                            * log to stderr as desired.\n                                            */\n#define RETRO_ENVIRONMENT_GET_PERF_INTERFACE 28\n                                           /* struct retro_perf_callback * --\n                                            * Gets an interface for performance counters. This is useful \n                                            * for performance logging in a cross-platform way and for detecting \n                                            * architecture-specific features, such as SIMD support.\n                                            */\n#define RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE 29\n                                           /* struct retro_location_callback * --\n                                            * Gets access to the location interface.\n                                            * The purpose of this interface is to be able to retrieve \n                                            * location-based information from the host device,\n                                            * such as current latitude / longitude.\n                                            */\n#define RETRO_ENVIRONMENT_GET_CONTENT_DIRECTORY 30 /* Old name, kept for compatibility. */\n#define RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY 30\n                                           /* const char ** --\n                                            * Returns the \"core assets\" directory of the frontend.\n                                            * This directory can be used to store specific assets that the \n                                            * core relies upon, such as art assets,\n                                            * input data, etc etc.\n                                            * The returned value can be NULL.\n                                            * If so, no such directory is defined,\n                                            * and it's up to the implementation to find a suitable directory.\n                                            */\n#define RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY 31\n                                           /* const char ** --\n                                            * Returns the \"save\" directory of the frontend.\n                                            * This directory can be used to store SRAM, memory cards, \n                                            * high scores, etc, if the libretro core\n                                            * cannot use the regular memory interface (retro_get_memory_data()).\n                                            *\n                                            * NOTE: libretro cores used to check GET_SYSTEM_DIRECTORY for \n                                            * similar things before.\n                                            * They should still check GET_SYSTEM_DIRECTORY if they want to \n                                            * be backwards compatible.\n                                            * The path here can be NULL. It should only be non-NULL if the \n                                            * frontend user has set a specific save path.\n                                            */\n#define RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO 32\n                                           /* const struct retro_system_av_info * --\n                                            * Sets a new av_info structure. This can only be called from \n                                            * within retro_run().\n                                            * This should *only* be used if the core is completely altering the \n                                            * internal resolutions, aspect ratios, timings, sampling rate, etc.\n                                            * Calling this can require a full reinitialization of video/audio \n                                            * drivers in the frontend,\n                                            *\n                                            * so it is important to call it very sparingly, and usually only with \n                                            * the users explicit consent.\n                                            * An eventual driver reinitialize will happen so that video and \n                                            * audio callbacks\n                                            * happening after this call within the same retro_run() call will \n                                            * target the newly initialized driver.\n                                            *\n                                            * This callback makes it possible to support configurable resolutions \n                                            * in games, which can be useful to\n                                            * avoid setting the \"worst case\" in max_width/max_height.\n                                            *\n                                            * ***HIGHLY RECOMMENDED*** Do not call this callback every time \n                                            * resolution changes in an emulator core if it's\n                                            * expected to be a temporary change, for the reasons of possible \n                                            * driver reinitialization.\n                                            * This call is not a free pass for not trying to provide \n                                            * correct values in retro_get_system_av_info(). If you need to change \n                                            * things like aspect ratio or nominal width/height, \n                                            * use RETRO_ENVIRONMENT_SET_GEOMETRY, which is a softer variant \n                                            * of SET_SYSTEM_AV_INFO.\n                                            *\n                                            * If this returns false, the frontend does not acknowledge a \n                                            * changed av_info struct.\n                                            */\n#define RETRO_ENVIRONMENT_SET_PROC_ADDRESS_CALLBACK 33\n                                           /* const struct retro_get_proc_address_interface * --\n                                            * Allows a libretro core to announce support for the \n                                            * get_proc_address() interface.\n                                            * This interface allows for a standard way to extend libretro where \n                                            * use of environment calls are too indirect,\n                                            * e.g. for cases where the frontend wants to call directly into the core.\n                                            *\n                                            * If a core wants to expose this interface, SET_PROC_ADDRESS_CALLBACK \n                                            * **MUST** be called from within retro_set_environment().\n                                            */\n#define RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO 34\n                                           /* const struct retro_subsystem_info * --\n                                            * This environment call introduces the concept of libretro \"subsystems\".\n                                            * A subsystem is a variant of a libretro core which supports \n                                            * different kinds of games.\n                                            * The purpose of this is to support e.g. emulators which might \n                                            * have special needs, e.g. Super Nintendo's Super GameBoy, Sufami Turbo.\n                                            * It can also be used to pick among subsystems in an explicit way \n                                            * if the libretro implementation is a multi-system emulator itself.\n                                            *\n                                            * Loading a game via a subsystem is done with retro_load_game_special(),\n                                            * and this environment call allows a libretro core to expose which \n                                            * subsystems are supported for use with retro_load_game_special().\n                                            * A core passes an array of retro_game_special_info which is terminated \n                                            * with a zeroed out retro_game_special_info struct.\n                                            *\n                                            * If a core wants to use this functionality, SET_SUBSYSTEM_INFO\n                                            * **MUST** be called from within retro_set_environment().\n                                            */\n#define RETRO_ENVIRONMENT_SET_CONTROLLER_INFO 35\n                                           /* const struct retro_controller_info * --\n                                            * This environment call lets a libretro core tell the frontend \n                                            * which controller types are recognized in calls to \n                                            * retro_set_controller_port_device().\n                                            *\n                                            * Some emulators such as Super Nintendo\n                                            * support multiple lightgun types which must be specifically \n                                            * selected from.\n                                            * It is therefore sometimes necessary for a frontend to be able \n                                            * to tell the core about a special kind of input device which is \n                                            * not covered by the libretro input API.\n                                            *\n                                            * In order for a frontend to understand the workings of an input device,\n                                            * it must be a specialized type\n                                            * of the generic device types already defined in the libretro API.\n                                            *\n                                            * Which devices are supported can vary per input port.\n                                            * The core must pass an array of const struct retro_controller_info which \n                                            * is terminated with a blanked out struct. Each element of the struct \n                                            * corresponds to an ascending port index to \n                                            * retro_set_controller_port_device().\n                                            * Even if special device types are set in the libretro core, \n                                            * libretro should only poll input based on the base input device types.\n                                            */\n#define RETRO_ENVIRONMENT_SET_MEMORY_MAPS (36 | RETRO_ENVIRONMENT_EXPERIMENTAL)\n                                           /* const struct retro_memory_map * --\n                                            * This environment call lets a libretro core tell the frontend \n                                            * about the memory maps this core emulates.\n                                            * This can be used to implement, for example, cheats in a core-agnostic way.\n                                            *\n                                            * Should only be used by emulators; it doesn't make much sense for \n                                            * anything else.\n                                            * It is recommended to expose all relevant pointers through \n                                            * retro_get_memory_* as well.\n                                            *\n                                            * Can be called from retro_init and retro_load_game.\n                                            */\n#define RETRO_ENVIRONMENT_SET_GEOMETRY 37\n                                           /* const struct retro_game_geometry * --\n                                            * This environment call is similar to SET_SYSTEM_AV_INFO for changing \n                                            * video parameters, but provides a guarantee that drivers will not be \n                                            * reinitialized.\n                                            * This can only be called from within retro_run().\n                                            *\n                                            * The purpose of this call is to allow a core to alter nominal \n                                            * width/heights as well as aspect ratios on-the-fly, which can be \n                                            * useful for some emulators to change in run-time.\n                                            *\n                                            * max_width/max_height arguments are ignored and cannot be changed\n                                            * with this call as this could potentially require a reinitialization or a \n                                            * non-constant time operation.\n                                            * If max_width/max_height are to be changed, SET_SYSTEM_AV_INFO is required.\n                                            *\n                                            * A frontend must guarantee that this environment call completes in \n                                            * constant time.\n                                            */\n#define RETRO_ENVIRONMENT_GET_USERNAME 38 \n                                           /* const char **\n                                            * Returns the specified username of the frontend, if specified by the user.\n                                            * This username can be used as a nickname for a core that has online facilities \n                                            * or any other mode where personalization of the user is desirable.\n                                            * The returned value can be NULL.\n                                            * If this environ callback is used by a core that requires a valid username, \n                                            * a default username should be specified by the core.\n                                            */\n#define RETRO_ENVIRONMENT_GET_LANGUAGE 39\n                                           /* unsigned * --\n                                            * Returns the specified language of the frontend, if specified by the user.\n                                            * It can be used by the core for localization purposes.\n                                            */\n#define RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER (40 | RETRO_ENVIRONMENT_EXPERIMENTAL)\n                                           /* struct retro_framebuffer * --\n                                            * Returns a preallocated framebuffer which the core can use for rendering\n                                            * the frame into when not using SET_HW_RENDER.\n                                            * The framebuffer returned from this call must not be used\n                                            * after the current call to retro_run() returns.\n                                            *\n                                            * The goal of this call is to allow zero-copy behavior where a core\n                                            * can render directly into video memory, avoiding extra bandwidth cost by copying\n                                            * memory from core to video memory.\n                                            *\n                                            * If this call succeeds and the core renders into it,\n                                            * the framebuffer pointer and pitch can be passed to retro_video_refresh_t.\n                                            * If the buffer from GET_CURRENT_SOFTWARE_FRAMEBUFFER is to be used,\n                                            * the core must pass the exact\n                                            * same pointer as returned by GET_CURRENT_SOFTWARE_FRAMEBUFFER;\n                                            * i.e. passing a pointer which is offset from the\n                                            * buffer is undefined. The width, height and pitch parameters\n                                            * must also match exactly to the values obtained from GET_CURRENT_SOFTWARE_FRAMEBUFFER.\n                                            *\n                                            * It is possible for a frontend to return a different pixel format\n                                            * than the one used in SET_PIXEL_FORMAT. This can happen if the frontend\n                                            * needs to perform conversion.\n                                            *\n                                            * It is still valid for a core to render to a different buffer\n                                            * even if GET_CURRENT_SOFTWARE_FRAMEBUFFER succeeds.\n                                            *\n                                            * A frontend must make sure that the pointer obtained from this function is\n                                            * writeable (and readable).\n                                            */\n\nenum retro_hw_render_interface_type\n{\n   RETRO_HW_RENDER_INTERFACE_VULKAN = 0,\n   RETRO_HW_RENDER_INTERFACE_DUMMY = INT_MAX\n};\n\n/* Base struct. All retro_hw_render_interface_* types\n * contain at least these fields. */\nstruct retro_hw_render_interface\n{\n   enum retro_hw_render_interface_type interface_type;\n   unsigned interface_version;\n};\n#define RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE (41 | RETRO_ENVIRONMENT_EXPERIMENTAL)\n                                           /* const struct retro_hw_render_interface ** --\n                                            * Returns an API specific rendering interface for accessing API specific data.\n                                            * Not all HW rendering APIs support or need this.\n                                            * The contents of the returned pointer is specific to the rendering API\n                                            * being used. See the various headers like libretro_vulkan.h, etc.\n                                            *\n                                            * GET_HW_RENDER_INTERFACE cannot be called before context_reset has been called.\n                                            * Similarly, after context_destroyed callback returns,\n                                            * the contents of the HW_RENDER_INTERFACE are invalidated.\n                                            */\n\n#define RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS (42 | RETRO_ENVIRONMENT_EXPERIMENTAL)\n                                           /* const bool * --\n                                            * If true, the libretro implementation supports achievements\n                                            * either via memory descriptors set with RETRO_ENVIRONMENT_SET_MEMORY_MAPS\n                                            * or via retro_get_memory_data/retro_get_memory_size.\n                                            *\n                                            * This must be called before the first call to retro_run.\n                                            */\n\nenum retro_hw_render_context_negotiation_interface_type\n{\n   RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN = 0,\n   RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_DUMMY = INT_MAX\n};\n\n/* Base struct. All retro_hw_render_context_negotiation_interface_* types\n * contain at least these fields. */\nstruct retro_hw_render_context_negotiation_interface\n{\n   enum retro_hw_render_context_negotiation_interface_type interface_type;\n   unsigned interface_version;\n};\n#define RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE (43 | RETRO_ENVIRONMENT_EXPERIMENTAL)\n                                           /* const struct retro_hw_render_context_negotiation_interface * --\n                                            * Sets an interface which lets the libretro core negotiate with frontend how a context is created.\n                                            * The semantics of this interface depends on which API is used in SET_HW_RENDER earlier.\n                                            * This interface will be used when the frontend is trying to create a HW rendering context,\n                                            * so it will be used after SET_HW_RENDER, but before the context_reset callback.\n                                            */\n\n#define RETRO_MEMDESC_CONST     (1 << 0)   /* The frontend will never change this memory area once retro_load_game has returned. */\n#define RETRO_MEMDESC_BIGENDIAN (1 << 1)   /* The memory area contains big endian data. Default is little endian. */\n#define RETRO_MEMDESC_ALIGN_2   (1 << 16)  /* All memory access in this area is aligned to their own size, or 2, whichever is smaller. */\n#define RETRO_MEMDESC_ALIGN_4   (2 << 16)\n#define RETRO_MEMDESC_ALIGN_8   (3 << 16)\n#define RETRO_MEMDESC_MINSIZE_2 (1 << 24)  /* All memory in this region is accessed at least 2 bytes at the time. */\n#define RETRO_MEMDESC_MINSIZE_4 (2 << 24)\n#define RETRO_MEMDESC_MINSIZE_8 (3 << 24)\nstruct retro_memory_descriptor\n{\n   uint64_t flags;\n\n   /* Pointer to the start of the relevant ROM or RAM chip.\n    * It's strongly recommended to use 'offset' if possible, rather than \n    * doing math on the pointer.\n    *\n    * If the same byte is mapped my multiple descriptors, their descriptors \n    * must have the same pointer.\n    * If 'start' does not point to the first byte in the pointer, put the \n    * difference in 'offset' instead.\n    *\n    * May be NULL if there's nothing usable here (e.g. hardware registers and \n    * open bus). No flags should be set if the pointer is NULL.\n    * It's recommended to minimize the number of descriptors if possible,\n    * but not mandatory. */\n   void *ptr;\n   size_t offset;\n\n   /* This is the location in the emulated address space \n    * where the mapping starts. */\n   size_t start;\n\n   /* Which bits must be same as in 'start' for this mapping to apply.\n    * The first memory descriptor to claim a certain byte is the one \n    * that applies.\n    * A bit which is set in 'start' must also be set in this.\n    * Can be zero, in which case each byte is assumed mapped exactly once. \n    * In this case, 'len' must be a power of two. */\n   size_t select;\n\n   /* If this is nonzero, the set bits are assumed not connected to the \n    * memory chip's address pins. */\n   size_t disconnect;\n\n   /* This one tells the size of the current memory area.\n    * If, after start+disconnect are applied, the address is higher than \n    * this, the highest bit of the address is cleared.\n    *\n    * If the address is still too high, the next highest bit is cleared.\n    * Can be zero, in which case it's assumed to be infinite (as limited \n    * by 'select' and 'disconnect'). */\n   size_t len;\n\n   /* To go from emulated address to physical address, the following \n    * order applies:\n    * Subtract 'start', pick off 'disconnect', apply 'len', add 'offset'. */\n\n   /* The address space name must consist of only a-zA-Z0-9_-, \n    * should be as short as feasible (maximum length is 8 plus the NUL),\n    * and may not be any other address space plus one or more 0-9A-F \n    * at the end.\n    * However, multiple memory descriptors for the same address space is \n    * allowed, and the address space name can be empty. NULL is treated \n    * as empty.\n    *\n    * Address space names are case sensitive, but avoid lowercase if possible.\n    * The same pointer may exist in multiple address spaces.\n    *\n    * Examples:\n    * blank+blank - valid (multiple things may be mapped in the same namespace)\n    * 'Sp'+'Sp' - valid (multiple things may be mapped in the same namespace)\n    * 'A'+'B' - valid (neither is a prefix of each other)\n    * 'S'+blank - valid ('S' is not in 0-9A-F)\n    * 'a'+blank - valid ('a' is not in 0-9A-F)\n    * 'a'+'A' - valid (neither is a prefix of each other)\n    * 'AR'+blank - valid ('R' is not in 0-9A-F)\n    * 'ARB'+blank - valid (the B can't be part of the address either, because \n    *                      there is no namespace 'AR')\n    * blank+'B' - not valid, because it's ambigous which address space B1234 \n    *             would refer to.\n    * The length can't be used for that purpose; the frontend may want \n    * to append arbitrary data to an address, without a separator. */\n   const char *addrspace;\n\n   /* TODO: When finalizing this one, add a description field, which should be\n    * \"WRAM\" or something roughly equally long. */\n\n   /* TODO: When finalizing this one, replace 'select' with 'limit', which tells\n    * which bits can vary and still refer to the same address (limit = ~select).\n    * TODO: limit? range? vary? something else? */\n\n   /* TODO: When finalizing this one, if 'len' is above what 'select' (or\n    * 'limit') allows, it's bankswitched. Bankswitched data must have both 'len'\n    * and 'select' != 0, and the mappings don't tell how the system switches the\n    * banks. */\n\n   /* TODO: When finalizing this one, fix the 'len' bit removal order.\n    * For len=0x1800, pointer 0x1C00 should go to 0x1400, not 0x0C00.\n    * Algorithm: Take bits highest to lowest, but if it goes above len, clear\n    * the most recent addition and continue on the next bit.\n    * TODO: Can the above be optimized? Is \"remove the lowest bit set in both\n    * pointer and 'len'\" equivalent? */\n   \n   /* TODO: Some emulators (MAME?) emulate big endian systems by only accessing\n    * the emulated memory in 32-bit chunks, native endian. But that's nothing\n    * compared to Darek Mihocka <http://www.emulators.com/docs/nx07_vm101.htm>\n    * (section Emulation 103 - Nearly Free Byte Reversal) - he flips the ENTIRE\n    * RAM backwards! I'll want to represent both of those, via some flags.\n    * \n    * I suspect MAME either didn't think of that idea, or don't want the #ifdef.\n    * Not sure which, nor do I really care. */\n   \n   /* TODO: Some of those flags are unused and/or don't really make sense. Clean\n    * them up. */\n};\n\n/* The frontend may use the largest value of 'start'+'select' in a \n * certain namespace to infer the size of the address space.\n *\n * If the address space is larger than that, a mapping with .ptr=NULL \n * should be at the end of the array, with .select set to all ones for \n * as long as the address space is big.\n *\n * Sample descriptors (minus .ptr, and RETRO_MEMFLAG_ on the flags):\n * SNES WRAM:\n * .start=0x7E0000, .len=0x20000\n * (Note that this must be mapped before the ROM in most cases; some of the \n * ROM mappers \n * try to claim $7E0000, or at least $7E8000.)\n * SNES SPC700 RAM:\n * .addrspace=\"S\", .len=0x10000\n * SNES WRAM mirrors:\n * .flags=MIRROR, .start=0x000000, .select=0xC0E000, .len=0x2000\n * .flags=MIRROR, .start=0x800000, .select=0xC0E000, .len=0x2000\n * SNES WRAM mirrors, alternate equivalent descriptor:\n * .flags=MIRROR, .select=0x40E000, .disconnect=~0x1FFF\n * (Various similar constructions can be created by combining parts of \n * the above two.)\n * SNES LoROM (512KB, mirrored a couple of times):\n * .flags=CONST, .start=0x008000, .select=0x408000, .disconnect=0x8000, .len=512*1024\n * .flags=CONST, .start=0x400000, .select=0x400000, .disconnect=0x8000, .len=512*1024\n * SNES HiROM (4MB):\n * .flags=CONST,                 .start=0x400000, .select=0x400000, .len=4*1024*1024\n * .flags=CONST, .offset=0x8000, .start=0x008000, .select=0x408000, .len=4*1024*1024\n * SNES ExHiROM (8MB):\n * .flags=CONST, .offset=0,                  .start=0xC00000, .select=0xC00000, .len=4*1024*1024\n * .flags=CONST, .offset=4*1024*1024,        .start=0x400000, .select=0xC00000, .len=4*1024*1024\n * .flags=CONST, .offset=0x8000,             .start=0x808000, .select=0xC08000, .len=4*1024*1024\n * .flags=CONST, .offset=4*1024*1024+0x8000, .start=0x008000, .select=0xC08000, .len=4*1024*1024\n * Clarify the size of the address space:\n * .ptr=NULL, .select=0xFFFFFF\n * .len can be implied by .select in many of them, but was included for clarity.\n */\n\nstruct retro_memory_map\n{\n   const struct retro_memory_descriptor *descriptors;\n   unsigned num_descriptors;\n};\n\nstruct retro_controller_description\n{\n   /* Human-readable description of the controller. Even if using a generic \n    * input device type, this can be set to the particular device type the \n    * core uses. */\n   const char *desc;\n\n   /* Device type passed to retro_set_controller_port_device(). If the device \n    * type is a sub-class of a generic input device type, use the \n    * RETRO_DEVICE_SUBCLASS macro to create an ID.\n    *\n    * E.g. RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1). */\n   unsigned id;\n};\n\nstruct retro_controller_info\n{\n   const struct retro_controller_description *types;\n   unsigned num_types;\n};\n\nstruct retro_subsystem_memory_info\n{\n   /* The extension associated with a memory type, e.g. \"psram\". */\n   const char *extension;\n\n   /* The memory type for retro_get_memory(). This should be at \n    * least 0x100 to avoid conflict with standardized \n    * libretro memory types. */\n   unsigned type;\n};\n\nstruct retro_subsystem_rom_info\n{\n   /* Describes what the content is (SGB BIOS, GB ROM, etc). */\n   const char *desc;\n\n   /* Same definition as retro_get_system_info(). */\n   const char *valid_extensions;\n\n   /* Same definition as retro_get_system_info(). */\n   bool need_fullpath;\n\n   /* Same definition as retro_get_system_info(). */\n   bool block_extract;\n\n   /* This is set if the content is required to load a game. \n    * If this is set to false, a zeroed-out retro_game_info can be passed. */\n   bool required;\n\n   /* Content can have multiple associated persistent \n    * memory types (retro_get_memory()). */\n   const struct retro_subsystem_memory_info *memory;\n   unsigned num_memory;\n};\n\nstruct retro_subsystem_info\n{\n   /* Human-readable string of the subsystem type, e.g. \"Super GameBoy\" */\n   const char *desc;\n\n   /* A computer friendly short string identifier for the subsystem type.\n    * This name must be [a-z].\n    * E.g. if desc is \"Super GameBoy\", this can be \"sgb\".\n    * This identifier can be used for command-line interfaces, etc.\n    */\n   const char *ident;\n\n   /* Infos for each content file. The first entry is assumed to be the \n    * \"most significant\" content for frontend purposes.\n    * E.g. with Super GameBoy, the first content should be the GameBoy ROM, \n    * as it is the most \"significant\" content to a user.\n    * If a frontend creates new file paths based on the content used \n    * (e.g. savestates), it should use the path for the first ROM to do so. */\n   const struct retro_subsystem_rom_info *roms;\n\n   /* Number of content files associated with a subsystem. */\n   unsigned num_roms;\n   \n   /* The type passed to retro_load_game_special(). */\n   unsigned id;\n};\n\ntypedef void (RETRO_CALLCONV *retro_proc_address_t)(void);\n\n/* libretro API extension functions:\n * (None here so far).\n *\n * Get a symbol from a libretro core.\n * Cores should only return symbols which are actual \n * extensions to the libretro API.\n *\n * Frontends should not use this to obtain symbols to standard \n * libretro entry points (static linking or dlsym).\n *\n * The symbol name must be equal to the function name, \n * e.g. if void retro_foo(void); exists, the symbol must be called \"retro_foo\".\n * The returned function pointer must be cast to the corresponding type.\n */\ntypedef retro_proc_address_t (RETRO_CALLCONV *retro_get_proc_address_t)(const char *sym);\n\nstruct retro_get_proc_address_interface\n{\n   retro_get_proc_address_t get_proc_address;\n};\n\nenum retro_log_level\n{\n   RETRO_LOG_DEBUG = 0,\n   RETRO_LOG_INFO,\n   RETRO_LOG_WARN,\n   RETRO_LOG_ERROR,\n\n   RETRO_LOG_DUMMY = INT_MAX\n};\n\n/* Logging function. Takes log level argument as well. */\ntypedef void (RETRO_CALLCONV *retro_log_printf_t)(enum retro_log_level level,\n      const char *fmt, ...);\n\nstruct retro_log_callback\n{\n   retro_log_printf_t log;\n};\n\n/* Performance related functions */\n\n/* ID values for SIMD CPU features */\n#define RETRO_SIMD_SSE      (1 << 0)\n#define RETRO_SIMD_SSE2     (1 << 1)\n#define RETRO_SIMD_VMX      (1 << 2)\n#define RETRO_SIMD_VMX128   (1 << 3)\n#define RETRO_SIMD_AVX      (1 << 4)\n#define RETRO_SIMD_NEON     (1 << 5)\n#define RETRO_SIMD_SSE3     (1 << 6)\n#define RETRO_SIMD_SSSE3    (1 << 7)\n#define RETRO_SIMD_MMX      (1 << 8)\n#define RETRO_SIMD_MMXEXT   (1 << 9)\n#define RETRO_SIMD_SSE4     (1 << 10)\n#define RETRO_SIMD_SSE42    (1 << 11)\n#define RETRO_SIMD_AVX2     (1 << 12)\n#define RETRO_SIMD_VFPU     (1 << 13)\n#define RETRO_SIMD_PS       (1 << 14)\n#define RETRO_SIMD_AES      (1 << 15)\n#define RETRO_SIMD_VFPV3    (1 << 16)\n#define RETRO_SIMD_VFPV4    (1 << 17)\n#define RETRO_SIMD_POPCNT   (1 << 18)\n#define RETRO_SIMD_MOVBE    (1 << 19)\n#define RETRO_SIMD_CMOV     (1 << 20)\n\ntypedef uint64_t retro_perf_tick_t;\ntypedef int64_t retro_time_t;\n\nstruct retro_perf_counter\n{\n   const char *ident;\n   retro_perf_tick_t start;\n   retro_perf_tick_t total;\n   retro_perf_tick_t call_cnt;\n\n   bool registered;\n};\n\n/* Returns current time in microseconds.\n * Tries to use the most accurate timer available.\n */\ntypedef retro_time_t (RETRO_CALLCONV *retro_perf_get_time_usec_t)(void);\n\n/* A simple counter. Usually nanoseconds, but can also be CPU cycles.\n * Can be used directly if desired (when creating a more sophisticated \n * performance counter system).\n * */\ntypedef retro_perf_tick_t (RETRO_CALLCONV *retro_perf_get_counter_t)(void);\n\n/* Returns a bit-mask of detected CPU features (RETRO_SIMD_*). */\ntypedef uint64_t (RETRO_CALLCONV *retro_get_cpu_features_t)(void);\n\n/* Asks frontend to log and/or display the state of performance counters.\n * Performance counters can always be poked into manually as well.\n */\ntypedef void (RETRO_CALLCONV *retro_perf_log_t)(void);\n\n/* Register a performance counter.\n * ident field must be set with a discrete value and other values in \n * retro_perf_counter must be 0.\n * Registering can be called multiple times. To avoid calling to \n * frontend redundantly, you can check registered field first. */\ntypedef void (RETRO_CALLCONV *retro_perf_register_t)(struct retro_perf_counter *counter);\n\n/* Starts a registered counter. */\ntypedef void (RETRO_CALLCONV *retro_perf_start_t)(struct retro_perf_counter *counter);\n\n/* Stops a registered counter. */\ntypedef void (RETRO_CALLCONV *retro_perf_stop_t)(struct retro_perf_counter *counter);\n\n/* For convenience it can be useful to wrap register, start and stop in macros.\n * E.g.:\n * #ifdef LOG_PERFORMANCE\n * #define RETRO_PERFORMANCE_INIT(perf_cb, name) static struct retro_perf_counter name = {#name}; if (!name.registered) perf_cb.perf_register(&(name))\n * #define RETRO_PERFORMANCE_START(perf_cb, name) perf_cb.perf_start(&(name))\n * #define RETRO_PERFORMANCE_STOP(perf_cb, name) perf_cb.perf_stop(&(name))\n * #else\n * ... Blank macros ...\n * #endif\n *\n * These can then be used mid-functions around code snippets.\n *\n * extern struct retro_perf_callback perf_cb;  * Somewhere in the core.\n *\n * void do_some_heavy_work(void)\n * {\n *    RETRO_PERFORMANCE_INIT(cb, work_1;\n *    RETRO_PERFORMANCE_START(cb, work_1);\n *    heavy_work_1();\n *    RETRO_PERFORMANCE_STOP(cb, work_1);\n *\n *    RETRO_PERFORMANCE_INIT(cb, work_2);\n *    RETRO_PERFORMANCE_START(cb, work_2);\n *    heavy_work_2();\n *    RETRO_PERFORMANCE_STOP(cb, work_2);\n * }\n *\n * void retro_deinit(void)\n * {\n *    perf_cb.perf_log();  * Log all perf counters here for example.\n * }\n */\n\nstruct retro_perf_callback\n{\n   retro_perf_get_time_usec_t    get_time_usec;\n   retro_get_cpu_features_t      get_cpu_features;\n\n   retro_perf_get_counter_t      get_perf_counter;\n   retro_perf_register_t         perf_register;\n   retro_perf_start_t            perf_start;\n   retro_perf_stop_t             perf_stop;\n   retro_perf_log_t              perf_log;\n};\n\n/* FIXME: Document the sensor API and work out behavior.\n * It will be marked as experimental until then.\n */\nenum retro_sensor_action\n{\n   RETRO_SENSOR_ACCELEROMETER_ENABLE = 0,\n   RETRO_SENSOR_ACCELEROMETER_DISABLE,\n\n   RETRO_SENSOR_DUMMY = INT_MAX\n};\n\n/* Id values for SENSOR types. */\n#define RETRO_SENSOR_ACCELEROMETER_X 0\n#define RETRO_SENSOR_ACCELEROMETER_Y 1\n#define RETRO_SENSOR_ACCELEROMETER_Z 2\n\ntypedef bool (RETRO_CALLCONV *retro_set_sensor_state_t)(unsigned port, \n      enum retro_sensor_action action, unsigned rate);\n\ntypedef float (RETRO_CALLCONV *retro_sensor_get_input_t)(unsigned port, unsigned id);\n\nstruct retro_sensor_interface\n{\n   retro_set_sensor_state_t set_sensor_state;\n   retro_sensor_get_input_t get_sensor_input;\n};\n\nenum retro_camera_buffer\n{\n   RETRO_CAMERA_BUFFER_OPENGL_TEXTURE = 0,\n   RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER,\n\n   RETRO_CAMERA_BUFFER_DUMMY = INT_MAX\n};\n\n/* Starts the camera driver. Can only be called in retro_run(). */\ntypedef bool (RETRO_CALLCONV *retro_camera_start_t)(void);\n\n/* Stops the camera driver. Can only be called in retro_run(). */\ntypedef void (RETRO_CALLCONV *retro_camera_stop_t)(void);\n\n/* Callback which signals when the camera driver is initialized \n * and/or deinitialized.\n * retro_camera_start_t can be called in initialized callback.\n */\ntypedef void (RETRO_CALLCONV *retro_camera_lifetime_status_t)(void);\n\n/* A callback for raw framebuffer data. buffer points to an XRGB8888 buffer.\n * Width, height and pitch are similar to retro_video_refresh_t.\n * First pixel is top-left origin.\n */\ntypedef void (RETRO_CALLCONV *retro_camera_frame_raw_framebuffer_t)(const uint32_t *buffer, \n      unsigned width, unsigned height, size_t pitch);\n\n/* A callback for when OpenGL textures are used.\n *\n * texture_id is a texture owned by camera driver.\n * Its state or content should be considered immutable, except for things like \n * texture filtering and clamping.\n *\n * texture_target is the texture target for the GL texture.\n * These can include e.g. GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE, and possibly \n * more depending on extensions.\n *\n * affine points to a packed 3x3 column-major matrix used to apply an affine \n * transform to texture coordinates. (affine_matrix * vec3(coord_x, coord_y, 1.0))\n * After transform, normalized texture coord (0, 0) should be bottom-left \n * and (1, 1) should be top-right (or (width, height) for RECTANGLE).\n *\n * GL-specific typedefs are avoided here to avoid relying on gl.h in \n * the API definition.\n */\ntypedef void (RETRO_CALLCONV *retro_camera_frame_opengl_texture_t)(unsigned texture_id, \n      unsigned texture_target, const float *affine);\n\nstruct retro_camera_callback\n{\n   /* Set by libretro core. \n    * Example bitmask: caps = (1 << RETRO_CAMERA_BUFFER_OPENGL_TEXTURE) | (1 << RETRO_CAMERA_BUFFER_RAW_FRAMEBUFFER).\n    */\n   uint64_t caps; \n\n   /* Desired resolution for camera. Is only used as a hint. */\n   unsigned width;\n   unsigned height;\n\n   /* Set by frontend. */\n   retro_camera_start_t start;\n   retro_camera_stop_t stop;\n\n   /* Set by libretro core if raw framebuffer callbacks will be used. */\n   retro_camera_frame_raw_framebuffer_t frame_raw_framebuffer;\n\n   /* Set by libretro core if OpenGL texture callbacks will be used. */\n   retro_camera_frame_opengl_texture_t frame_opengl_texture; \n\n   /* Set by libretro core. Called after camera driver is initialized and \n    * ready to be started.\n    * Can be NULL, in which this callback is not called.\n    */\n   retro_camera_lifetime_status_t initialized;\n\n   /* Set by libretro core. Called right before camera driver is \n    * deinitialized.\n    * Can be NULL, in which this callback is not called.\n    */\n   retro_camera_lifetime_status_t deinitialized;\n};\n\n/* Sets the interval of time and/or distance at which to update/poll \n * location-based data.\n *\n * To ensure compatibility with all location-based implementations,\n * values for both interval_ms and interval_distance should be provided.\n *\n * interval_ms is the interval expressed in milliseconds.\n * interval_distance is the distance interval expressed in meters.\n */\ntypedef void (RETRO_CALLCONV *retro_location_set_interval_t)(unsigned interval_ms,\n      unsigned interval_distance);\n\n/* Start location services. The device will start listening for changes to the\n * current location at regular intervals (which are defined with \n * retro_location_set_interval_t). */\ntypedef bool (RETRO_CALLCONV *retro_location_start_t)(void);\n\n/* Stop location services. The device will stop listening for changes \n * to the current location. */\ntypedef void (RETRO_CALLCONV *retro_location_stop_t)(void);\n\n/* Get the position of the current location. Will set parameters to \n * 0 if no new  location update has happened since the last time. */\ntypedef bool (RETRO_CALLCONV *retro_location_get_position_t)(double *lat, double *lon,\n      double *horiz_accuracy, double *vert_accuracy);\n\n/* Callback which signals when the location driver is initialized \n * and/or deinitialized.\n * retro_location_start_t can be called in initialized callback.\n */\ntypedef void (RETRO_CALLCONV *retro_location_lifetime_status_t)(void);\n\nstruct retro_location_callback\n{\n   retro_location_start_t         start;\n   retro_location_stop_t          stop;\n   retro_location_get_position_t  get_position;\n   retro_location_set_interval_t  set_interval;\n\n   retro_location_lifetime_status_t initialized;\n   retro_location_lifetime_status_t deinitialized;\n};\n\nenum retro_rumble_effect\n{\n   RETRO_RUMBLE_STRONG = 0,\n   RETRO_RUMBLE_WEAK = 1,\n\n   RETRO_RUMBLE_DUMMY = INT_MAX\n};\n\n/* Sets rumble state for joypad plugged in port 'port'. \n * Rumble effects are controlled independently,\n * and setting e.g. strong rumble does not override weak rumble.\n * Strength has a range of [0, 0xffff].\n *\n * Returns true if rumble state request was honored. \n * Calling this before first retro_run() is likely to return false. */\ntypedef bool (RETRO_CALLCONV *retro_set_rumble_state_t)(unsigned port, \n      enum retro_rumble_effect effect, uint16_t strength);\n\nstruct retro_rumble_interface\n{\n   retro_set_rumble_state_t set_rumble_state;\n};\n\n/* Notifies libretro that audio data should be written. */\ntypedef void (RETRO_CALLCONV *retro_audio_callback_t)(void);\n\n/* True: Audio driver in frontend is active, and callback is \n * expected to be called regularily.\n * False: Audio driver in frontend is paused or inactive. \n * Audio callback will not be called until set_state has been \n * called with true.\n * Initial state is false (inactive).\n */\ntypedef void (RETRO_CALLCONV *retro_audio_set_state_callback_t)(bool enabled);\n\nstruct retro_audio_callback\n{\n   retro_audio_callback_t callback;\n   retro_audio_set_state_callback_t set_state;\n};\n\n/* Notifies a libretro core of time spent since last invocation \n * of retro_run() in microseconds.\n *\n * It will be called right before retro_run() every frame.\n * The frontend can tamper with timing to support cases like \n * fast-forward, slow-motion and framestepping.\n *\n * In those scenarios the reference frame time value will be used. */\ntypedef int64_t retro_usec_t;\ntypedef void (RETRO_CALLCONV *retro_frame_time_callback_t)(retro_usec_t usec);\nstruct retro_frame_time_callback\n{\n   retro_frame_time_callback_t callback;\n   /* Represents the time of one frame. It is computed as \n    * 1000000 / fps, but the implementation will resolve the \n    * rounding to ensure that framestepping, etc is exact. */\n   retro_usec_t reference;\n};\n\n/* Pass this to retro_video_refresh_t if rendering to hardware.\n * Passing NULL to retro_video_refresh_t is still a frame dupe as normal.\n * */\n#define RETRO_HW_FRAME_BUFFER_VALID ((void*)-1)\n\n/* Invalidates the current HW context.\n * Any GL state is lost, and must not be deinitialized explicitly.\n * If explicit deinitialization is desired by the libretro core,\n * it should implement context_destroy callback.\n * If called, all GPU resources must be reinitialized.\n * Usually called when frontend reinits video driver.\n * Also called first time video driver is initialized, \n * allowing libretro core to initialize resources.\n */\ntypedef void (RETRO_CALLCONV *retro_hw_context_reset_t)(void);\n\n/* Gets current framebuffer which is to be rendered to.\n * Could change every frame potentially.\n */\ntypedef uintptr_t (RETRO_CALLCONV *retro_hw_get_current_framebuffer_t)(void);\n\n/* Get a symbol from HW context. */\ntypedef retro_proc_address_t (RETRO_CALLCONV *retro_hw_get_proc_address_t)(const char *sym);\n\nenum retro_hw_context_type\n{\n   RETRO_HW_CONTEXT_NONE             = 0,\n   /* OpenGL 2.x. Driver can choose to use latest compatibility context. */\n   RETRO_HW_CONTEXT_OPENGL           = 1, \n   /* OpenGL ES 2.0. */\n   RETRO_HW_CONTEXT_OPENGLES2        = 2,\n   /* Modern desktop core GL context. Use version_major/\n    * version_minor fields to set GL version. */\n   RETRO_HW_CONTEXT_OPENGL_CORE      = 3,\n   /* OpenGL ES 3.0 */\n   RETRO_HW_CONTEXT_OPENGLES3        = 4,\n   /* OpenGL ES 3.1+. Set version_major/version_minor. For GLES2 and GLES3,\n    * use the corresponding enums directly. */\n   RETRO_HW_CONTEXT_OPENGLES_VERSION = 5,\n\n   /* Vulkan, see RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE. */\n   RETRO_HW_CONTEXT_VULKAN           = 6,\n\n   RETRO_HW_CONTEXT_DUMMY = INT_MAX\n};\n\nstruct retro_hw_render_callback\n{\n   /* Which API to use. Set by libretro core. */\n   enum retro_hw_context_type context_type;\n\n   /* Called when a context has been created or when it has been reset.\n    * An OpenGL context is only valid after context_reset() has been called.\n    *\n    * When context_reset is called, OpenGL resources in the libretro \n    * implementation are guaranteed to be invalid.\n    *\n    * It is possible that context_reset is called multiple times during an \n    * application lifecycle.\n    * If context_reset is called without any notification (context_destroy),\n    * the OpenGL context was lost and resources should just be recreated\n    * without any attempt to \"free\" old resources.\n    */\n   retro_hw_context_reset_t context_reset;\n\n   /* Set by frontend.\n    * TODO: This is rather obsolete. The frontend should not\n    * be providing preallocated framebuffers. */\n   retro_hw_get_current_framebuffer_t get_current_framebuffer;\n\n   /* Set by frontend. */\n   retro_hw_get_proc_address_t get_proc_address;\n\n   /* Set if render buffers should have depth component attached.\n    * TODO: Obsolete. */\n   bool depth;\n\n   /* Set if stencil buffers should be attached.\n    * TODO: Obsolete. */\n   bool stencil;\n\n   /* If depth and stencil are true, a packed 24/8 buffer will be added. \n    * Only attaching stencil is invalid and will be ignored. */\n\n   /* Use conventional bottom-left origin convention. If false, \n    * standard libretro top-left origin semantics are used.\n    * TODO: Move to GL specific interface. */\n   bool bottom_left_origin;\n   \n   /* Major version number for core GL context or GLES 3.1+. */\n   unsigned version_major;\n\n   /* Minor version number for core GL context or GLES 3.1+. */\n   unsigned version_minor;\n\n   /* If this is true, the frontend will go very far to avoid \n    * resetting context in scenarios like toggling fullscreen, etc.\n    * TODO: Obsolete? Maybe frontend should just always assume this ...\n    */\n   bool cache_context;\n\n   /* The reset callback might still be called in extreme situations \n    * such as if the context is lost beyond recovery.\n    *\n    * For optimal stability, set this to false, and allow context to be \n    * reset at any time.\n    */\n   \n   /* A callback to be called before the context is destroyed in a \n    * controlled way by the frontend. */\n   retro_hw_context_reset_t context_destroy;\n\n   /* OpenGL resources can be deinitialized cleanly at this step.\n    * context_destroy can be set to NULL, in which resources will \n    * just be destroyed without any notification.\n    *\n    * Even when context_destroy is non-NULL, it is possible that \n    * context_reset is called without any destroy notification.\n    * This happens if context is lost by external factors (such as \n    * notified by GL_ARB_robustness).\n    *\n    * In this case, the context is assumed to be already dead,\n    * and the libretro implementation must not try to free any OpenGL \n    * resources in the subsequent context_reset.\n    */\n\n   /* Creates a debug context. */\n   bool debug_context;\n};\n\n/* Callback type passed in RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. \n * Called by the frontend in response to keyboard events.\n * down is set if the key is being pressed, or false if it is being released.\n * keycode is the RETROK value of the char.\n * character is the text character of the pressed key. (UTF-32).\n * key_modifiers is a set of RETROKMOD values or'ed together.\n *\n * The pressed/keycode state can be indepedent of the character.\n * It is also possible that multiple characters are generated from a \n * single keypress.\n * Keycode events should be treated separately from character events.\n * However, when possible, the frontend should try to synchronize these.\n * If only a character is posted, keycode should be RETROK_UNKNOWN.\n *\n * Similarily if only a keycode event is generated with no corresponding \n * character, character should be 0.\n */\ntypedef void (RETRO_CALLCONV *retro_keyboard_event_t)(bool down, unsigned keycode, \n      uint32_t character, uint16_t key_modifiers);\n\nstruct retro_keyboard_callback\n{\n   retro_keyboard_event_t callback;\n};\n\n/* Callbacks for RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE.\n * Should be set for implementations which can swap out multiple disk \n * images in runtime.\n *\n * If the implementation can do this automatically, it should strive to do so.\n * However, there are cases where the user must manually do so.\n *\n * Overview: To swap a disk image, eject the disk image with \n * set_eject_state(true).\n * Set the disk index with set_image_index(index). Insert the disk again \n * with set_eject_state(false).\n */\n\n/* If ejected is true, \"ejects\" the virtual disk tray.\n * When ejected, the disk image index can be set.\n */\ntypedef bool (RETRO_CALLCONV *retro_set_eject_state_t)(bool ejected);\n\n/* Gets current eject state. The initial state is 'not ejected'. */\ntypedef bool (RETRO_CALLCONV *retro_get_eject_state_t)(void);\n\n/* Gets current disk index. First disk is index 0.\n * If return value is >= get_num_images(), no disk is currently inserted.\n */\ntypedef unsigned (RETRO_CALLCONV *retro_get_image_index_t)(void);\n\n/* Sets image index. Can only be called when disk is ejected.\n * The implementation supports setting \"no disk\" by using an \n * index >= get_num_images().\n */\ntypedef bool (RETRO_CALLCONV *retro_set_image_index_t)(unsigned index);\n\n/* Gets total number of images which are available to use. */\ntypedef unsigned (RETRO_CALLCONV *retro_get_num_images_t)(void);\n\nstruct retro_game_info;\n\n/* Replaces the disk image associated with index.\n * Arguments to pass in info have same requirements as retro_load_game().\n * Virtual disk tray must be ejected when calling this.\n *\n * Replacing a disk image with info = NULL will remove the disk image \n * from the internal list.\n * As a result, calls to get_image_index() can change.\n *\n * E.g. replace_image_index(1, NULL), and previous get_image_index() \n * returned 4 before.\n * Index 1 will be removed, and the new index is 3.\n */\ntypedef bool (RETRO_CALLCONV *retro_replace_image_index_t)(unsigned index,\n      const struct retro_game_info *info);\n\n/* Adds a new valid index (get_num_images()) to the internal disk list.\n * This will increment subsequent return values from get_num_images() by 1.\n * This image index cannot be used until a disk image has been set \n * with replace_image_index. */\ntypedef bool (RETRO_CALLCONV *retro_add_image_index_t)(void);\n\nstruct retro_disk_control_callback\n{\n   retro_set_eject_state_t set_eject_state;\n   retro_get_eject_state_t get_eject_state;\n\n   retro_get_image_index_t get_image_index;\n   retro_set_image_index_t set_image_index;\n   retro_get_num_images_t  get_num_images;\n\n   retro_replace_image_index_t replace_image_index;\n   retro_add_image_index_t add_image_index;\n};\n\nenum retro_pixel_format\n{\n   /* 0RGB1555, native endian.\n    * 0 bit must be set to 0.\n    * This pixel format is default for compatibility concerns only.\n    * If a 15/16-bit pixel format is desired, consider using RGB565. */\n   RETRO_PIXEL_FORMAT_0RGB1555 = 0,\n\n   /* XRGB8888, native endian.\n    * X bits are ignored. */\n   RETRO_PIXEL_FORMAT_XRGB8888 = 1,\n\n   /* RGB565, native endian.\n    * This pixel format is the recommended format to use if a 15/16-bit\n    * format is desired as it is the pixel format that is typically \n    * available on a wide range of low-power devices.\n    *\n    * It is also natively supported in APIs like OpenGL ES. */\n   RETRO_PIXEL_FORMAT_RGB565   = 2,\n\n   /* Ensure sizeof() == sizeof(int). */\n   RETRO_PIXEL_FORMAT_UNKNOWN  = INT_MAX\n};\n\nstruct retro_message\n{\n   const char *msg;        /* Message to be displayed. */\n   unsigned    frames;     /* Duration in frames of message. */\n};\n\n/* Describes how the libretro implementation maps a libretro input bind\n * to its internal input system through a human readable string.\n * This string can be used to better let a user configure input. */\nstruct retro_input_descriptor\n{\n   /* Associates given parameters with a description. */\n   unsigned port;\n   unsigned device;\n   unsigned index;\n   unsigned id;\n\n   /* Human readable description for parameters.\n    * The pointer must remain valid until\n    * retro_unload_game() is called. */\n   const char *description; \n};\n\nstruct retro_system_info\n{\n   /* All pointers are owned by libretro implementation, and pointers must \n    * remain valid until retro_deinit() is called. */\n\n   const char *library_name;      /* Descriptive name of library. Should not \n                                   * contain any version numbers, etc. */\n   const char *library_version;   /* Descriptive version of core. */\n\n   const char *valid_extensions;  /* A string listing probably content \n                                   * extensions the core will be able to \n                                   * load, separated with pipe.\n                                   * I.e. \"bin|rom|iso\".\n                                   * Typically used for a GUI to filter \n                                   * out extensions. */\n\n   /* If true, retro_load_game() is guaranteed to provide a valid pathname \n    * in retro_game_info::path.\n    * ::data and ::size are both invalid.\n    *\n    * If false, ::data and ::size are guaranteed to be valid, but ::path \n    * might not be valid.\n    *\n    * This is typically set to true for libretro implementations that must \n    * load from file.\n    * Implementations should strive for setting this to false, as it allows \n    * the frontend to perform patching, etc. */\n   bool        need_fullpath;                                       \n\n   /* If true, the frontend is not allowed to extract any archives before \n    * loading the real content.\n    * Necessary for certain libretro implementations that load games \n    * from zipped archives. */\n   bool        block_extract;     \n};\n\nstruct retro_game_geometry\n{\n   unsigned base_width;    /* Nominal video width of game. */\n   unsigned base_height;   /* Nominal video height of game. */\n   unsigned max_width;     /* Maximum possible width of game. */\n   unsigned max_height;    /* Maximum possible height of game. */\n\n   float    aspect_ratio;  /* Nominal aspect ratio of game. If\n                            * aspect_ratio is <= 0.0, an aspect ratio\n                            * of base_width / base_height is assumed.\n                            * A frontend could override this setting,\n                            * if desired. */\n};\n\nstruct retro_system_timing\n{\n   double fps;             /* FPS of video content. */\n   double sample_rate;     /* Sampling rate of audio. */\n};\n\nstruct retro_system_av_info\n{\n   struct retro_game_geometry geometry;\n   struct retro_system_timing timing;\n};\n\nstruct retro_variable\n{\n   /* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE.\n    * If NULL, obtains the complete environment string if more \n    * complex parsing is necessary.\n    * The environment string is formatted as key-value pairs \n    * delimited by semicolons as so:\n    * \"key1=value1;key2=value2;...\"\n    */\n   const char *key;\n   \n   /* Value to be obtained. If key does not exist, it is set to NULL. */\n   const char *value;\n};\n\nstruct retro_game_info\n{\n   const char *path;       /* Path to game, UTF-8 encoded.\n                            * Usually used as a reference.\n                            * May be NULL if rom was loaded from stdin\n                            * or similar. \n                            * retro_system_info::need_fullpath guaranteed \n                            * that this path is valid. */\n   const void *data;       /* Memory buffer of loaded game. Will be NULL \n                            * if need_fullpath was set. */\n   size_t      size;       /* Size of memory buffer. */\n   const char *meta;       /* String of implementation specific meta-data. */\n};\n\n#define RETRO_MEMORY_ACCESS_WRITE (1 << 0)\n   /* The core will write to the buffer provided by retro_framebuffer::data. */\n#define RETRO_MEMORY_ACCESS_READ (1 << 1)\n   /* The core will read from retro_framebuffer::data. */\n#define RETRO_MEMORY_TYPE_CACHED (1 << 0)\n   /* The memory in data is cached.\n    * If not cached, random writes and/or reading from the buffer is expected to be very slow. */\nstruct retro_framebuffer\n{\n   void *data;                      /* The framebuffer which the core can render into.\n                                       Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER.\n                                       The initial contents of data are unspecified. */\n   unsigned width;                  /* The framebuffer width used by the core. Set by core. */\n   unsigned height;                 /* The framebuffer height used by the core. Set by core. */\n   size_t pitch;                    /* The number of bytes between the beginning of a scanline,\n                                       and beginning of the next scanline.\n                                       Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */\n   enum retro_pixel_format format;  /* The pixel format the core must use to render into data.\n                                       This format could differ from the format used in\n                                       SET_PIXEL_FORMAT.\n                                       Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */\n\n   unsigned access_flags;           /* How the core will access the memory in the framebuffer.\n                                       RETRO_MEMORY_ACCESS_* flags.\n                                       Set by core. */\n   unsigned memory_flags;           /* Flags telling core how the memory has been mapped.\n                                       RETRO_MEMORY_TYPE_* flags.\n                                       Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */\n};\n\n/* Callbacks */\n\n/* Environment callback. Gives implementations a way of performing \n * uncommon tasks. Extensible. */\ntypedef bool (RETRO_CALLCONV *retro_environment_t)(unsigned cmd, void *data);\n\n/* Render a frame. Pixel format is 15-bit 0RGB1555 native endian \n * unless changed (see RETRO_ENVIRONMENT_SET_PIXEL_FORMAT).\n *\n * Width and height specify dimensions of buffer.\n * Pitch specifices length in bytes between two lines in buffer.\n *\n * For performance reasons, it is highly recommended to have a frame \n * that is packed in memory, i.e. pitch == width * byte_per_pixel.\n * Certain graphic APIs, such as OpenGL ES, do not like textures \n * that are not packed in memory.\n */\ntypedef void (RETRO_CALLCONV *retro_video_refresh_t)(const void *data, unsigned width,\n      unsigned height, size_t pitch);\n\n/* Renders a single audio frame. Should only be used if implementation \n * generates a single sample at a time.\n * Format is signed 16-bit native endian.\n */\ntypedef void (RETRO_CALLCONV *retro_audio_sample_t)(int16_t left, int16_t right);\n\n/* Renders multiple audio frames in one go.\n *\n * One frame is defined as a sample of left and right channels, interleaved.\n * I.e. int16_t buf[4] = { l, r, l, r }; would be 2 frames.\n * Only one of the audio callbacks must ever be used.\n */\ntypedef size_t (RETRO_CALLCONV *retro_audio_sample_batch_t)(const int16_t *data,\n      size_t frames);\n\n/* Polls input. */\ntypedef void (RETRO_CALLCONV *retro_input_poll_t)(void);\n\n/* Queries for input for player 'port'. device will be masked with \n * RETRO_DEVICE_MASK.\n *\n * Specialization of devices such as RETRO_DEVICE_JOYPAD_MULTITAP that \n * have been set with retro_set_controller_port_device()\n * will still use the higher level RETRO_DEVICE_JOYPAD to request input.\n */\ntypedef int16_t (RETRO_CALLCONV *retro_input_state_t)(unsigned port, unsigned device, \n      unsigned index, unsigned id);\n\n/* Sets callbacks. retro_set_environment() is guaranteed to be called \n * before retro_init().\n *\n * The rest of the set_* functions are guaranteed to have been called \n * before the first call to retro_run() is made. */\nRETRO_API void retro_set_environment(retro_environment_t);\nRETRO_API void retro_set_video_refresh(retro_video_refresh_t);\nRETRO_API void retro_set_audio_sample(retro_audio_sample_t);\nRETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t);\nRETRO_API void retro_set_input_poll(retro_input_poll_t);\nRETRO_API void retro_set_input_state(retro_input_state_t);\n\n/* Library global initialization/deinitialization. */\nRETRO_API void retro_init(void);\nRETRO_API void retro_deinit(void);\n\n/* Must return RETRO_API_VERSION. Used to validate ABI compatibility\n * when the API is revised. */\nRETRO_API unsigned retro_api_version(void);\n\n/* Gets statically known system info. Pointers provided in *info \n * must be statically allocated.\n * Can be called at any time, even before retro_init(). */\nRETRO_API void retro_get_system_info(struct retro_system_info *info);\n\n/* Gets information about system audio/video timings and geometry.\n * Can be called only after retro_load_game() has successfully completed.\n * NOTE: The implementation of this function might not initialize every \n * variable if needed.\n * E.g. geom.aspect_ratio might not be initialized if core doesn't \n * desire a particular aspect ratio. */\nRETRO_API void retro_get_system_av_info(struct retro_system_av_info *info);\n\n/* Sets device to be used for player 'port'.\n * By default, RETRO_DEVICE_JOYPAD is assumed to be plugged into all \n * available ports.\n * Setting a particular device type is not a guarantee that libretro cores \n * will only poll input based on that particular device type. It is only a \n * hint to the libretro core when a core cannot automatically detect the \n * appropriate input device type on its own. It is also relevant when a \n * core can change its behavior depending on device type. */\nRETRO_API void retro_set_controller_port_device(unsigned port, unsigned device);\n\n/* Resets the current game. */\nRETRO_API void retro_reset(void);\n\n/* Runs the game for one video frame.\n * During retro_run(), input_poll callback must be called at least once.\n * \n * If a frame is not rendered for reasons where a game \"dropped\" a frame,\n * this still counts as a frame, and retro_run() should explicitly dupe \n * a frame if GET_CAN_DUPE returns true.\n * In this case, the video callback can take a NULL argument for data.\n */\nRETRO_API void retro_run(void);\n\n/* Returns the amount of data the implementation requires to serialize \n * internal state (save states).\n * Between calls to retro_load_game() and retro_unload_game(), the \n * returned size is never allowed to be larger than a previous returned \n * value, to ensure that the frontend can allocate a save state buffer once.\n */\nRETRO_API size_t retro_serialize_size(void);\n\n/* Serializes internal state. If failed, or size is lower than\n * retro_serialize_size(), it should return false, true otherwise. */\nRETRO_API bool retro_serialize(void *data, size_t size);\nRETRO_API bool retro_unserialize(const void *data, size_t size);\n\nRETRO_API void retro_cheat_reset(void);\nRETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code);\n\n/* Loads a game. */\nRETRO_API bool retro_load_game(const struct retro_game_info *game);\n\n/* Loads a \"special\" kind of game. Should not be used,\n * except in extreme cases. */\nRETRO_API bool retro_load_game_special(\n  unsigned game_type,\n  const struct retro_game_info *info, size_t num_info\n);\n\n/* Unloads a currently loaded game. */\nRETRO_API void retro_unload_game(void);\n\n/* Gets region of game. */\nRETRO_API unsigned retro_get_region(void);\n\n/* Gets region of memory. */\nRETRO_API void *retro_get_memory_data(unsigned id);\nRETRO_API size_t retro_get_memory_size(unsigned id);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libretro/libretro_main.c",
    "content": "//\n// Copyright 2021 Timo Kloss, Antoine Fauroux\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"libretro_main.h\"\n#include <string.h>\n#include <stdlib.h>\n#include <stdarg.h>\n#include \"libretro.h\"\n#include \"core.h\"\n#include \"boot_intro.h\"\n\n#define SAMPLING_RATE 44100.0f\n#define VIDEO_PIXELS SCREEN_WIDTH * SCREEN_HEIGHT\n#define AUDIO_SAMPLES 1470\n\nstatic struct retro_log_callback logging;\nstatic retro_log_printf_t log;\nstatic retro_environment_t environment_callback;\nstatic retro_video_refresh_t video_refresh_callback;\nstatic retro_audio_sample_t audio_sample_callback;\nstatic retro_audio_sample_batch_t audio_sample_batch_callback;\nstatic retro_input_poll_t input_poll_callback;\nstatic retro_input_state_t input_state_callback;\n\nstatic uint32_t *pixels;\nstatic int16_t *audio_buf;\n\nstatic struct Core *core = NULL;\nstatic struct CoreDelegate coreDelegate;\nstatic struct CoreInput coreInput;\nstatic long ticks = 0;\nstatic bool hasInput = false;\nstatic bool hasUsedInputLastUpdate = false;\nstatic bool hasPressesPause = false;\nstatic bool hasPressesPauseLastUpdate = false;\nstatic bool messageShownUsingDisk = false;\nstatic enum MainState mainState = MainStateUndefined;\nstatic char *sourceCode = NULL;\n\nvoid bootNX(void);\nvoid runMainProgram(void);\n\nvoid interpreterDidFail(void *context, struct CoreError coreError);\nbool diskDriveWillAccess(void *context, struct DataManager *diskDataManager);\nvoid diskDriveDidSave(void *context, struct DataManager *diskDataManager);\nvoid diskDriveIsFull(void *context, struct DataManager *diskDataManager);\nvoid controlsDidChange(void *context, struct ControlsInfo controlsInfo);\nvoid persistentRamWillAccess(void *context, uint8_t *destination, int size);\nvoid persistentRamDidChange(void *context, uint8_t *data, int size);\n\n\n/* ======== LibRetro ======== */\n\nstatic void fallback_log(enum retro_log_level level, const char *fmt, ...)\n{\n   va_list va;\n   va_start(va, fmt);\n   vfprintf(stderr, fmt, va);\n   va_end(va);\n}\n\nvoid show_message(const char *txt)\n{\n    struct retro_message msg = { txt, 120 };\n    environment_callback(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);\n}\n\nvoid init_joysticks()\n{\n    struct retro_input_descriptor desc[] =\n    {\n        // Player 1\n        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, \"D-Pad Left\"},\n        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, \"D-Pad Up\"},\n        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, \"D-Pad Down\"},\n        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, \"D-Pad Right\"},\n        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, \"A\"},\n        {0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, \"B\"},\n        \n        // Player 2\n        {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, \"D-Pad Left\"},\n        {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, \"D-Pad Up\"},\n        {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, \"D-Pad Down\"},\n        {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, \"D-Pad Right\"},\n        {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, \"A\"},\n        {1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, \"B\"},\n        \n        {0}\n    };\n    \n    environment_callback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);\n}\n\n/**\n * Retrieve gamepad information from libretro.\n */\nbool update_gamepad(int player)\n{\n    // D-Pad\n    coreInput.gamepads[player].up = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP);\n    coreInput.gamepads[player].down = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN);\n    coreInput.gamepads[player].left = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT);\n    coreInput.gamepads[player].right = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT);\n\n    // A/B\n    coreInput.gamepads[player].buttonA = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A);\n    coreInput.gamepads[player].buttonB = input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B);\n    \n    // Pause (Start button)\n    if (input_state_callback(player, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START))\n    {\n        hasPressesPause = true;\n    }\n    \n    struct CoreInputGamepad gamepad = coreInput.gamepads[player];\n    return gamepad.buttonA || gamepad.buttonB || gamepad.up || gamepad.down || gamepad.left || gamepad.right;\n}\n\nint mouse_pointer_convert(float coord, float full)\n{\n    float max = 0x7fff;\n    return (int)((coord + max) / (max * 2.0f) * full);\n}\n\nbool update_mouse()\n{\n    // Get the Pointer X and Y, and convert it to screen position.\n    coreInput.touchX = mouse_pointer_convert(input_state_callback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X), SCREEN_WIDTH);\n    coreInput.touchY = mouse_pointer_convert(input_state_callback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y), SCREEN_HEIGHT);\n    \n    coreInput.touch = input_state_callback(0, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED);\n    \n    return coreInput.touch;\n}\n\nvoid keyboard_pressed(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers)\n{\n    if (down)\n    {\n        hasInput = true;\n        \n        if (character != 0)\n        {\n            // Text input\n            if (character >= RETROK_SPACE &&  character <= RETROK_UNDERSCORE)\n            {\n                coreInput.key = character;\n            }\n            if (character >= RETROK_a &&  character <= RETROK_z)\n            {\n                coreInput.key = character - 32;\n            }\n        }\n        \n        switch (keycode)\n        {\n        case RETROK_RETURN:\n            coreInput.key = CoreInputKeyReturn;\n            break;\n        case RETROK_BACKSPACE:\n            coreInput.key = CoreInputKeyBackspace;\n            break;\n        case RETROK_UP:\n            coreInput.key = CoreInputKeyUp;\n            break;\n        case RETROK_DOWN:\n            coreInput.key = CoreInputKeyDown;\n            break;\n        case RETROK_LEFT:\n            coreInput.key = CoreInputKeyLeft;\n            break;\n        case RETROK_RIGHT:\n            coreInput.key = CoreInputKeyRight;\n            break;\n        }\n    }\n}\n\n/* Sets callbacks. retro_set_environment() is guaranteed to be called\n * before retro_init().\n *\n * The rest of the set_* functions are guaranteed to have been called\n * before the first call to retro_run() is made. */\n\nRETRO_API void retro_set_environment(retro_environment_t callback)\n{\n    environment_callback = callback;\n    \n    bool no_content = false;\n    callback(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_content);\n    \n    if (callback(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))\n        log = logging.log;\n    else\n        log = fallback_log;\n    \n    enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;\n    callback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt);\n    \n    struct retro_keyboard_callback kcb = {\n        keyboard_pressed\n    };\n    callback(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &kcb);\n}\n\nRETRO_API void retro_set_video_refresh(retro_video_refresh_t callback)\n{\n    video_refresh_callback = callback;\n}\n\nRETRO_API void retro_set_audio_sample(retro_audio_sample_t callback)\n{\n    audio_sample_callback = callback;\n}\n\nRETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t callback)\n{\n    audio_sample_batch_callback = callback;\n}\n\nRETRO_API void retro_set_input_poll(retro_input_poll_t callback)\n{\n    input_poll_callback = callback;\n}\n\nRETRO_API void retro_set_input_state(retro_input_state_t callback)\n{\n    input_state_callback = callback;\n}\n\n/* Library global initialization/deinitialization. */\n\nRETRO_API void retro_init(void)\n{\n    log(RETRO_LOG_INFO, \"[LowRes NX] Initialization\\n\");\n    \n    core = calloc(1, sizeof(struct Core));\n    if (core)\n    {\n        core_init(core);\n        \n        coreDelegate.interpreterDidFail = interpreterDidFail;\n        coreDelegate.diskDriveWillAccess = diskDriveWillAccess;\n        coreDelegate.diskDriveDidSave = diskDriveDidSave;\n        coreDelegate.diskDriveIsFull = diskDriveIsFull;\n        coreDelegate.controlsDidChange = controlsDidChange;\n        coreDelegate.persistentRamWillAccess = persistentRamWillAccess;\n        coreDelegate.persistentRamDidChange = persistentRamDidChange;\n        \n        core_setDelegate(core, &coreDelegate);\n    }\n    \n    pixels = calloc(VIDEO_PIXELS, sizeof(uint32_t));\n    audio_buf = calloc(AUDIO_SAMPLES, sizeof(int16_t));\n    \n    init_joysticks();\n    \n    bootNX();\n}\n\nRETRO_API void retro_deinit(void)\n{\n    log(RETRO_LOG_INFO, \"[LowRes NX] Deinitialization\\n\");\n    \n    if (core)\n    {\n        core_deinit(core);\n        free(core);\n        core = NULL;\n    }\n    \n    if (pixels)\n    {\n        free(pixels);\n        pixels = NULL;\n    }\n    \n    if (audio_buf)\n    {\n        free(audio_buf);\n        audio_buf = NULL;\n    }\n}\n\n/* Must return RETRO_API_VERSION. Used to validate ABI compatibility\n * when the API is revised. */\nRETRO_API unsigned retro_api_version(void)\n{\n    return RETRO_API_VERSION;\n}\n\n/* Gets statically known system info. Pointers provided in *info\n * must be statically allocated.\n * Can be called at any time, even before retro_init(). */\nRETRO_API void retro_get_system_info(struct retro_system_info *info)\n{\n    memset(info, 0, sizeof(*info));\n    info->library_name     = \"LowRes NX\";\n    info->library_version  = CORE_VERSION;\n    info->need_fullpath    = false;\n    info->valid_extensions = \"nx\";\n}\n\n/* Gets information about system audio/video timings and geometry.\n * Can be called only after retro_load_game() has successfully completed.\n * NOTE: The implementation of this function might not initialize every\n * variable if needed.\n * E.g. geom.aspect_ratio might not be initialized if core doesn't\n * desire a particular aspect ratio. */\nRETRO_API void retro_get_system_av_info(struct retro_system_av_info *info)\n{\n    info->timing.fps = 60.0;\n    info->timing.sample_rate = SAMPLING_RATE;\n    \n    info->geometry.base_width = SCREEN_WIDTH;\n    info->geometry.base_height = SCREEN_HEIGHT;\n    info->geometry.max_width = SCREEN_WIDTH;\n    info->geometry.max_height = SCREEN_HEIGHT;\n    info->geometry.aspect_ratio = 0.0f;\n    \n}\n\n/* Sets device to be used for player 'port'.\n * By default, RETRO_DEVICE_JOYPAD is assumed to be plugged into all\n * available ports.\n * Setting a particular device type is not a guarantee that libretro cores\n * will only poll input based on that particular device type. It is only a\n * hint to the libretro core when a core cannot automatically detect the\n * appropriate input device type on its own. It is also relevant when a\n * core can change its behavior depending on device type. */\nRETRO_API void retro_set_controller_port_device(unsigned port, unsigned device)\n{\n}\n\n/* Resets the current game. */\nRETRO_API void retro_reset(void)\n{\n    log(RETRO_LOG_INFO, \"[LowRes NX] Reset\\n\");\n    runMainProgram();\n}\n\n/* Runs the game for one video frame.\n * During retro_run(), input_poll callback must be called at least once.\n *\n * If a frame is not rendered for reasons where a game \"dropped\" a frame,\n * this still counts as a frame, and retro_run() should explicitly dupe\n * a frame if GET_CAN_DUPE returns true.\n * In this case, the video callback can take a NULL argument for data.\n */\nRETRO_API void retro_run(void)\n{\n    input_poll_callback();\n    \n    if (core && pixels && audio_buf)\n    {\n        for (int i = 0; i < NUM_GAMEPADS; ++i)\n        {\n            if (update_gamepad(i))\n            {\n                hasInput = true;\n            }\n        }\n        \n        if (hasPressesPause && !hasPressesPauseLastUpdate)\n        {\n            coreInput.pause = true;\n        }\n        \n        if (update_mouse())\n        {\n            hasInput = true;\n        }\n        \n        switch (mainState)\n        {\n            case MainStateUndefined:\n                break;\n                \n            case MainStateBootIntro:\n                core_update(core, &coreInput);\n                if (machine_peek(core, bootIntroStateAddress) == BootIntroStateReadyToRun)\n                {\n                    machine_poke(core, bootIntroStateAddress, BootIntroStateDone);\n                    runMainProgram();\n                }\n                break;\n                \n            case MainStateRunningProgram:\n                core_update(core, &coreInput);\n                if (hasInput)\n                {\n                    if (core->interpreter->state == StateEnd)\n                    {\n                        show_message(\"End of program\");\n                    }\n                    else if (!coreInput.out_hasUsedInput && !hasUsedInputLastUpdate)\n                    {\n                        // user hints for controls\n                        union IOAttributes attr = core->machine->ioRegisters.attr;\n                        if (attr.touchEnabled && !attr.keyboardEnabled)\n                        {\n                            show_message(\"Touch/mouse expected\");\n                        }\n                        if (attr.keyboardEnabled && !attr.touchEnabled)\n                        {\n                            show_message(\"Keyboard expected\");\n                        }\n                        if (attr.gamepadsEnabled && !attr.keyboardEnabled)\n                        {\n                            show_message(\"Joypad expected\");\n                        }\n                    }\n                }\n                break;\n        }\n        \n        hasUsedInputLastUpdate = coreInput.out_hasUsedInput;\n        \n        video_renderScreen(core, pixels);\n        video_refresh_callback(pixels, SCREEN_WIDTH, SCREEN_HEIGHT, sizeof(uint32_t) * SCREEN_WIDTH);\n        \n        audio_renderAudio(core, audio_buf, AUDIO_SAMPLES, SAMPLING_RATE, 0);\n        audio_sample_batch_callback(audio_buf, AUDIO_SAMPLES / 2);\n    }\n    \n    hasInput = false;\n    \n    hasPressesPauseLastUpdate = hasPressesPause;\n    hasPressesPause = false;\n    \n    ++ticks;\n}\n\n/* Returns the amount of data the implementation requires to serialize\n * internal state (save states).\n * Between calls to retro_load_game() and retro_unload_game(), the\n * returned size is never allowed to be larger than a previous returned\n * value, to ensure that the frontend can allocate a save state buffer once.\n */\nRETRO_API size_t retro_serialize_size(void)\n{\n    return 0;\n}\n\n/* Serializes internal state. If failed, or size is lower than\n * retro_serialize_size(), it should return false, true otherwise. */\n\nRETRO_API bool retro_serialize(void *data, size_t size)\n{\n    return false;\n}\n\nRETRO_API bool retro_unserialize(const void *data, size_t size)\n{\n    return false;\n}\n\nRETRO_API void retro_cheat_reset(void)\n{\n}\n\nRETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code)\n{\n}\n\n/* Loads a game. */\nRETRO_API bool retro_load_game(const struct retro_game_info *game)\n{\n    log(RETRO_LOG_INFO, \"[LowRes NX] Load game\\n\");\n    \n    if (core && game && game->data)\n    {\n        sourceCode = calloc(1, game->size + 1); // +1 for terminator\n        if (sourceCode)\n        {\n            memcpy(sourceCode, game->data, game->size);\n            if (mainState == MainStateBootIntro)\n            {\n                machine_poke(core, bootIntroStateAddress, BootIntroStateProgramAvailable);\n            }\n            else\n            {\n                runMainProgram();\n            }\n            return true;\n        }\n    }\n    return false;\n}\n\n/* Loads a \"special\" kind of game. Should not be used,\n * except in extreme cases. */\nRETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info)\n{\n    return false;\n}\n\n/* Unloads a currently loaded game. */\nRETRO_API void retro_unload_game(void)\n{\n    if (core)\n    {\n        core_willSuspendProgram(core);\n    }\n    \n    if (sourceCode)\n    {\n        free(sourceCode);\n        sourceCode = NULL;\n    }\n}\n\n/* Gets region of game. */\nRETRO_API unsigned retro_get_region(void)\n{\n    return RETRO_REGION_NTSC;\n}\n\n/* Gets region of memory. */\nRETRO_API void *retro_get_memory_data(unsigned id)\n{\n    switch (id)\n    {\n        case RETRO_MEMORY_SAVE_RAM:\n            return core->machine->persistentRam;\n        default:\n            return NULL;\n    }\n}\n\nRETRO_API size_t retro_get_memory_size(unsigned id)\n{\n    switch (id)\n    {\n        case RETRO_MEMORY_SAVE_RAM:\n            return PERSISTENT_RAM_SIZE;\n        default:\n            return 0;\n    }\n}\n\n/* ======== LowRes NX ======== */\n\nvoid bootNX()\n{\n    if (!core) return;\n    \n    mainState = MainStateBootIntro;\n    \n    struct CoreError error = core_compileProgram(core, bootIntroSourceCode, true);\n    if (error.code != ErrorNone)\n    {\n        core_traceError(core, error);\n    }\n    \n    core->interpreter->debug = false;\n    core_willRunProgram(core, ticks / 60);\n}\n\nvoid runMainProgram()\n{\n    if (!core || !sourceCode) return;\n    \n    core_willSuspendProgram(core);\n    \n    struct CoreError error = core_compileProgram(core, sourceCode, false);\n    if (error.code != ErrorNone)\n    {\n        core_traceError(core, error);\n    }\n    else\n    {\n        core_willRunProgram(core, ticks / 60);\n        mainState = MainStateRunningProgram;\n    }\n    \n    messageShownUsingDisk = false;\n}\n\n/** Called on error */\nvoid interpreterDidFail(void *context, struct CoreError coreError)\n{\n    core_traceError(core, coreError);\n}\n\n/** Returns true if the disk is ready, false if not. In case of not, core_diskLoaded must be called when ready. */\nbool diskDriveWillAccess(void *context, struct DataManager *diskDataManager)\n{\n    if (!messageShownUsingDisk)\n    {\n        show_message(\"No virtual disk\");\n        messageShownUsingDisk = true;\n    }\n    return true;\n}\n\n/** Called when a disk data entry was saved */\nvoid diskDriveDidSave(void *context, struct DataManager *diskDataManager)\n{\n    show_message(\"No virtual disk\");\n}\n\n/** Called when a disk data entry was tried to be saved, but the disk is full */\nvoid diskDriveIsFull(void *context, struct DataManager *diskDataManager)\n{\n}\n\n/** Called when keyboard or gamepad settings changed */\nvoid controlsDidChange(void *context, struct ControlsInfo controlsInfo)\n{\n}\n\n/** Called when persistent RAM will be accessed the first time */\nvoid persistentRamWillAccess(void *context, uint8_t *destination, int size)\n{\n}\n\n/** Called when persistent RAM should be saved */\nvoid persistentRamDidChange(void *context, uint8_t *data, int size)\n{\n}\n"
  },
  {
    "path": "libretro/libretro_main.h",
    "content": "//\n// Copyright 2021 Timo Kloss, Antoine Fauroux\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef libretro_main_h\n#define libretro_main_h\n\n#include <stdio.h>\n\nenum MainState {\n    MainStateUndefined,\n    MainStateBootIntro,\n    MainStateRunningProgram\n};\n\n#endif /* libretro_main_h */\n"
  },
  {
    "path": "platform/GameShell/README.md",
    "content": "# ClockworkPi GameShell\n\nCompile the Linux version and copy it to /home/cpi/apps.\nCopy this folder to the GameShell to add LowRes NX to your menu.\n"
  },
  {
    "path": "platform/GameShell/home/cpi/apps/Menu/55_LowRes NX/action.config",
    "content": "ROM=/home/cpi/games/LowResNX\nEXT=nx\nLAUNCHER=/home/cpi/apps/LowResNX -disabledev yes -fullscreen yes -zoom 3 -mapping 1 -disabledelay yes\nTITLE=LowRes NX Games\n"
  },
  {
    "path": "platform/GameShell/home/cpi/games/LowResNX/LowRes Galaxy 2 (1.5).nx",
    "content": "'TITLE:   LOWRES GALAXY 2\n'AUTHOR:  TIMO KLOSS\n\nRANDOMIZE TIMER\n\n' SPRITES\n' 0 PLAYER SHIP\n' 1 JET\n' 2-9 PLAYER BULLETS\n' 10-25 ALIENS\n' 26-29 EXPLOSIONS\n' 30-39 ALIEN BULLETS\n\n'POINTS FOR ALIENS\nDIM GLOBAL POINTS(4)\nDATA 0,10,20,400,600\nFOR I=0 TO 4\n  READ POINTS(I)\nNEXT I\n\n'ALIENS\nDIM GLOBAL ALIENS(15,5)\n' 0 TYPE (0=DISABLED)\n' 1 TICK\n' 2 START Y\n' 3 HITS\n' 4 AMPLITUDE\n' 5 SPEED\n\n'ALIEN BULLETS\nDIM GLOBAL ABULLETS(9,3)\n' 0 X\n' 1 Y\n' 2 X VECTOR\n' 3 Y VECTOR\n\n'EXPLOSION TICKS\nDIM GLOBAL EXPLOSIONS(3)\n\nGLOBAL HIGHSCORE\nGLOBAL TICK,BGTICK\nGLOBAL LEVEL,SCORE,LIVES\nGLOBAL PX,PY\nGLOBAL SHIELD,HIDE,PEACE\nGLOBAL SHDELAY,HEAT,BULLET\nGLOBAL EXPLOSION\nGLOBAL ALIEN,ABULLET\nGLOBAL MSGTIMER\n\nFONT 128\n\nCALL DRAWGAMEBG\nON RASTER CALL RASTERFX\n\nGAMEPAD 1\n\n'READ FROM PERSISTENT RAM\nHIGHSCORE=PEEKL($E000)\n\n\nTITLE:\n\nTICK=0\n\nSPRITE OFF\nCALL DRAWTITLE\nSOUND SOURCE ROM(15)\nMUSIC 0\nREPEAT\n  INC BGTICK\n  IF TICK MOD 240=0 THEN\n    CALL SHOWMSG(\"PRESS ANY BUTTON\")\n  ELSE IF TICK MOD 240=120 THEN\n    CALL SHOWMSG(\"HIGHSCORE: \"+STR$(HIGHSCORE))\n  END IF\n  INC TICK\n  WAIT VBL\nUNTIL BUTTON TAP(0)\nSTOP\n\n\nGAME:\n\n'INIT PLAYER SPRITES\nSPRITE 0 PAL 4 SIZE 1\nSPRITE 1 PAL 5\nFOR I=2 TO 9\n  SPRITE I PAL 5\nNEXT I\n\n'INIT VARIABLES\nLIVES=5\nSCORE=0\nLEVEL=0\nPX=32\nPY=48\nSHIELD=120\nHIDE=0\nPEACE=0\nSHDELAY=0\nHEAT=0\nTICK=0\nBULLET=0\nALIEN=0\n\nFOR I=0 TO 15\n  ALIENS(I,0)=0\nNEXT I\nFOR I=0 TO 9\n  ABULLETS(I,0)=-32\nNEXT I\n\nCALL CLEAROVERLAYS\nCALL DRAWHUD\n\nCALL RESETSOUND\nSOUND SOURCE ROM(15)\nMUSIC 8\nSOUND SOURCE ROM(14)\n\n'GAME LOOP\nDO\n  'NEXT LEVEL?\n  IF LIVES>0 AND TICK MOD 1800=0 THEN\n    INC LEVEL\n    CALL SHOWMSG(\"LEVEL \"+STR$(LEVEL))\n    IF LEVEL>=2 THEN\n      CALL ADDSCORE(LEVEL*100)\n      TRACK 0,3\n    END IF\n  END IF\n\n  IF PEACE>0 THEN\n    'DO NOT SPAWN ALIENS\n    DEC PEACE\n  ELSE\n    'SPAWN SMALL ALIEN?\n    M=480/(LEVEL+3)\n    IF TICK MOD M=0 THEN\n      CALL SPAWNALIEN(1+RND(1))\n    END IF\n \n    'SPAWN BIG ALIEN?\n    M=5400/(LEVEL+2)\n    IF TICK MOD M=M\\2 THEN\n      CALL SPAWNALIEN(3+RND(1))\n    END IF\n  END IF\n \n  CALL UPDALIENS\n  CALL UPDBULLETS\n  CALL UPDALIENBULLETS\n\n  IF LIVES>0 THEN\n    CALL UPDPLAYER\n  ELSE\n    'GAME OVER\n    IF MSGTIMER>0 THEN\n      DEC MSGTIMER\n    ELSE\n      IF BUTTON TAP(0) THEN GOTO TITLE\n    END IF\n  END IF\n\n  CALL UPDEXPLOSIONS\n  CALL UPDMSG\n \n  INC TICK\n  INC BGTICK\n\n  WAIT VBL\nLOOP\n\nSUB ADDSCORE(P)\n  ADD SCORE,P\n  CALL DRAWHUD\nEND SUB\n\nSUB UPDPLAYER\n  IF HIDE>0 THEN\n    DEC HIDE\n    SPRITE OFF 0 TO 1\n    EXIT SUB\n  END IF\n\n  'PLAYER CONTROL\n  IF UP(0) AND PY>0 THEN DEC PY\n  IF DOWN(0) AND PY<112 THEN INC PY\n  IF LEFT(0) AND PX>8 THEN DEC PX\n  IF RIGHT(0) AND PX<128 THEN INC PX\n\n  'PLAYER SPRITE\n  IF SHIELD>0 AND SHIELD MOD 4<2 THEN\n    SPRITE OFF 0 TO 1\n  ELSE\n    SPRITE 0,PX,PY,1\n    SPRITE 1,PX-8,PY+5,19+INT((TICK MOD 16)/8)\n  END IF\n\n  'SHOOT?\n  SHDELAY=SHDELAY-1\n  IF BUTTON(0) THEN\n    IF SHDELAY<=0 THEN\n      SPRITE BULLET+2,PX+8,PY+11,3\n      BULLET=(BULLET+1) MOD 8\n      PLAY 2,50+RND*2 SOUND 0\n      HEAT=HEAT+1\n      IF HEAT>=5 THEN\n        SHDELAY=30\n        HEAT=0\n      ELSE\n        SHDELAY=8\n      END IF\n    END IF\n  ELSE\n    HEAT=0\n  END IF\n\n  IF SHIELD>0 THEN\n    DEC SHIELD\n  ELSE\n\n    'PLAYER HIT BY BULLET?\n    IF SPRITE HIT(0,30 TO 39) THEN\n      SPRITE OFF HIT\n      ABULLETS(HIT-30,0)=-32\n      CALL LOSESHIP\n    END IF\n\n    'COLLISION WITH ALIEN?\n    IF SPRITE HIT(0,10 TO 25) THEN\n      CALL LOSESHIP\n    END IF\n\n  END IF\nEND SUB\n\nSUB LOSESHIP\n  DEC LIVES\n  CALL DRAWHUD\n  SPRITE OFF 0 TO 1\n  PLAY 2,30 SOUND 4\n  CALL EXPLODE(PX,PY,0)\n  CALL EXPLODE(PX-8+RND*16,PY-8+RND*16,10)\n  CALL EXPLODE(PX-8+RND*16,PY-8+RND*16,30)\n  IF LIVES=0 THEN\n    CALL DRAWGAMEOVER\n    IF SCORE>HIGHSCORE THEN\n      HIGHSCORE=SCORE\n      CALL SHOWMSG(\"NEW HIGHSCORE!\")\n      'WRITE TO PERSISTENT RAM\n      POKEL $E000,HIGHSCORE\n    ELSE\n      CALL SHOWMSG(\"HIGHSCORE: \"+STR$(HIGHSCORE))\n    END IF\n    SOUND SOURCE ROM(15)\n    MUSIC 32\n    SOUND SOURCE ROM(14)\n    MSGTIMER=100\n  ELSE\n    HIDE=120\n    SHIELD=120\n    PEACE=180\n  END IF\nEND SUB\n\nSUB UPDBULLETS\n  FOR I=2 TO 9\n    IF SPRITE.X(I)>=0 THEN\n      SPRITE I,SPRITE.X(I)+3,,\n      IF SPRITE.X(I)>160 THEN SPRITE OFF I\n    END IF\n  NEXT I\nEND SUB\n\nSUB SPAWNALIEN(TYPE)\n  I=ALIEN\n  IF ALIENS(I,0)>0 THEN EXIT SUB\n  ALIENS(I,0)=TYPE\n  ALIENS(I,1)=0\n  ALIENS(I,2)=16+RND*80\n  IF TYPE=1 THEN\n    ALIENS(I,3)=1\n    ALIENS(I,4)=RND*12\n    ALIENS(I,5)=0.5\n  ELSE IF TYPE=2 THEN\n    ALIENS(I,3)=1\n    ALIENS(I,4)=RND*20\n    ALIENS(I,5)=0.35\n  ELSE IF TYPE=3 THEN\n    ALIENS(I,3)=4\n    ALIENS(I,4)=RND*32\n    ALIENS(I,5)=0.25\n  ELSE IF TYPE=4 THEN\n    ALIENS(I,3)=8\n    ALIENS(I,4)=RND*40\n    ALIENS(I,5)=0.15\n  END IF\n  ALIEN=(ALIEN+1) MOD 16\nEND SUB\n\nSUB UPDALIENS\n  FOR I=0 TO 15\n    IF ALIENS(I,0)>0 THEN\n      CALL UPDALIEN(I)\n    END IF\n  NEXT I\nEND SUB\n\nSUB UPDALIEN(I)\n  TYPE=ALIENS(I,0)\n  N=10+I\n  ALIENS(I,1)=ALIENS(I,1)+1\n  T=ALIENS(I,1)\n  X=160-T*ALIENS(I,5)\n  X=X-SIN(T*0.03)*24\n \n  'STILL ON SCREEN?\n  IF X>-32 THEN\n  \n    'UPDATE SPRITE\n    Y=ALIENS(I,2)\n    AY=ALIENS(I,4)\n    IF TYPE MOD 2=0 THEN P=6 ELSE P=7\n    IF TYPE<=2 THEN\n      S=0\n      C=10+INT((T MOD 24)/12)\n      Y=Y+SIN(T/20)*AY\n    ELSE\n      S=1\n      C=6+INT((T MOD 32)/16)*2\n      Y=Y+SIN(T/60)*AY\n    END IF\n    SPRITE N PAL P SIZE S\n    SPRITE N,X,Y,C\n\n    'HIT BY BULLET?\n    IF SPRITE HIT(N,2 TO 9) THEN\n      SPRITE OFF HIT\n      SPRITE N PAL 3\n      ALIENS(I,3)=ALIENS(I,3)-1\n      IF ALIENS(I,3)=0 THEN\n        CALL ADDSCORE(POINTS(TYPE))\n        SPRITE OFF N\n        ALIENS(I,0)=0\n        IF S=0 THEN\n          CALL EXPLODE(X-4,Y-4,0)\n        ELSE\n          CALL EXPLODE(X,Y,0)\n        END IF\n        IF TYPE>=3 THEN PLAY 3,40 SOUND 5\n      ELSE\n        PLAY 3,45 SOUND 2\n      END IF\n    END IF\n  \n    'SHOOT?\n    IF PEACE=0 AND X>50 AND T MOD 120=30 THEN\n      CALL ALIENSHOOT(X+4,Y+4)\n    END IF\n \n  ELSE\n    'OUT OF SCREEN, RESET\n    SPRITE OFF N\n    ALIENS(I,0)=0\n  END IF\nEND SUB\n\nSUB ALIENSHOOT(X,Y)\n  I=ABULLET\n  IF ABULLETS(I,0)>-32 THEN EXIT SUB\n  N=I+30\n  PLAY 3,45+RND*2 SOUND 1\n  SPRITE N,X,Y,4\n  SPRITE N PAL 3\n  ABULLETS(I,0)=X\n  ABULLETS(I,1)=Y\n  U=PX+6-X\n  V=PY+6-Y\n  W=SQR(U*U+V*V)\n  ABULLETS(I,2)=U/W\n  ABULLETS(I,3)=V/W\n  ABULLET=(ABULLET+1) MOD 3\nEND SUB\n\nSUB UPDALIENBULLETS\n  FOR I=0 TO 9\n    N=I+30\n    X=ABULLETS(I,0)\n    Y=ABULLETS(I,1)\n    X=X+ABULLETS(I,2)\n    Y=Y+ABULLETS(I,3)\n    IF X>=-8 AND X<160 AND Y>=-8 AND Y<128 THEN\n      ABULLETS(I,0)=X\n      ABULLETS(I,1)=Y\n      SPRITE N,X,Y,\n    ELSE\n      ABULLETS(I,0)=-32\n      SPRITE OFF N\n    END IF\n  NEXT I\nEND SUB\n\nSUB EXPLODE(X,Y,DELAY)\n  N=EXPLOSION+40\n  SPRITE N PAL 5 SIZE 0\n  SPRITE N,X,Y,0\n  EXPLOSIONS(EXPLOSION)=20+DELAY\n  EXPLOSION=(EXPLOSION+1) MOD 4\nEND SUB\n\nSUB UPDEXPLOSIONS\n  FOR I=0 TO 3\n    T=EXPLOSIONS(I)\n    IF T>0 THEN\n      IF T<=20 THEN\n        N=I+40\n        SPRITE N SIZE 1\n        SPRITE N,,,32+((20-T)\\5)*2\n        IF T=20 THEN PLAY 3,25+RND*5 SOUND 3\n      END IF\n      DEC T\n      IF T=0 THEN SPRITE OFF N\n      EXPLOSIONS(I)=T\n    END IF\n  NEXT I\nEND SUB\n\nSUB DRAWHUD\n  BG 0\n  PAL 4\n  PRIO 1\n  BG FILL 0,0 TO 4,0 CHAR 0\n  FOR I=0 TO LIVES-1\n    CELL I,0,5\n  NEXT I\n  PAL 0\n  NUMBER 15,0,SCORE,5\nEND SUB\n\nSUB DRAWGAMEBG\n  BG SOURCE ROM(3)\n  BG 0\n  BG COPY 0,0,32,16 TO 0,0\n  BG 1\n  BG COPY 0,16,32,16 TO 0,0\nEND SUB\n\nSUB DRAWTITLE\n  BG 0\n  BG FILL 0,0 TO 19,6 CHAR 0\n  BG SOURCE ROM(4)\n  BG COPY 0,0,20,6 TO 4,1\nEND SUB\n\nSUB DRAWGAMEOVER\n  BG 0\n  BG SOURCE ROM(4)\n  BG COPY 0,7,8,2 TO 1,5\n  BG COPY 9,7,8,2 TO 11,5\nEND SUB\n\nSUB CLEAROVERLAYS\n  BG 0\n  BG FILL 0,0 TO 19,6 CHAR 0\n  BG 1\n  BG FILL 0,11 TO 19,11 CHAR 0\nEND SUB\n\nSUB SHOWMSG(MSG$)\n  BG 1\n  PAL 0\n  PRIO 1\n  L=LEN(MSG$)\n  BG FILL 0,11 TO 19,11 CHAR 0\n  TEXT (20-L)/2,11,MSG$\n  MSGTIMER=120\nEND SUB\n\nSUB UPDMSG\n  IF MSGTIMER>0 THEN\n    DEC MSGTIMER\n    IF MSGTIMER=0 THEN\n      BG 1\n      BG FILL 0,11 TO 19,11 CHAR 0\n    END IF\n  END IF\nEND SUB\n\nSUB RESETSOUND\n  STOP\n  FOR I=0 TO 3\n    VOLUME I,15,%11\n  NEXT I\nEND SUB\n\nSUB RASTERFX\n  'STARS AND FOREGROUND HILLS\n  IF RASTER=0 THEN\n    SCROLL 1,BGTICK/6,0\n  ELSE IF RASTER=88 THEN\n    SCROLL 1,0,0\n  ELSE IF RASTER=96 THEN\n    SCROLL 1,BGTICK,0\n  END IF\n\n  'STATUS BAR AND PLANET SURFACE\n  IF RASTER=0 THEN\n    SCROLL 0,0,0\n  ELSE IF RASTER=58 THEN\n    SCROLL 0,BGTICK*0.5,0\n  ELSE IF RASTER=80 THEN\n    SCROLL 0,BGTICK*5/8,0\n  ELSE IF RASTER=96 THEN\n    SCROLL 0,BGTICK*6/8,0\n  ELSE IF RASTER=112 THEN\n    SCROLL 0,BGTICK*7/8,0\n  END IF\nEND SUB\n\n\n#1:MAIN PALETTES\n003F1B0500261101003B2612003F3E39\n001F2A15003C3006003F3121003F1612\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n000040600078FF54000040607078FFFF\n000000000000FC02000000000000FCFE\n04E733040000000006F83C0600000000\n60F0F060000000006090906000000000\n406070821E764040406070FEF0766040\n00041030400C8E0E00071F2F7F73F5F5\n002008040260E1E000E0F8FCFE9E5F5F\n041028400C8E0E06071F377F73F5F5F9\n2008040260E1E0C0E0F8FCFE9E5F5F3F\n0042816600006642007EFFBDFFFF6642\n42816666000066247EFFBDBDFF7E6624\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\nA8FF007802604000FFFF7C787C604000\n7FBDFA7C00000000FFC3867C00000000\n100409A30904100010060EBC0E061000\n40120413041240004012071C07124000\n00000000000000000000000000000000\n040080403C383018FBFFFF7F3C383018\n400001023C1C0C18BFFFFFFE3C1C0C18\n800040203C1C0E02FF7F7F3F3C1C0E02\n010002043C387040FFFEFEFC3C387040\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n000001070F1F1F3F0000010608101020\n000080E0F0F8F8FC0000806010080804\n071F3F7F7FFFFFFF0610204000808000\nE0F8FCFEFEFFFFFF6008040200010100\n071F3F7C78F1E2E404102043078F1E1C\nE0F81C02780102002008E4FEFE030301\n020816285020A000030F1E387060E0C0\n4010280400000000C0F0380400000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n3F1F1F0F070100002010100806010000\nFCF8F8F0E08000000408081060800000\nFFFFFF7F7F3F1F070080800040201006\nFFFFFFFEFEFCF8E00001010002040860\nC0C8C86868301A043838B81858281607\n00000000000000000000000000000000\n0080204020100000C0C0606020100000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00400000000002000000000000000200\n00000000000000000000020000000000\n000008002A0008000000080836080800\n1F604080868C8C8C001F3F7F7E7C7C7C\n6018180C0C000000E0F8F8FCFC000000\n1F604080868C8C8C001F3F7F7F7C7C7C\n6018180C0C0C8C8CE0F8F8FCFCFC7C7C\nEC8C8C8C8C8C8C8C1C7C7C7C7C7C7C7C\n00000000000000000000000000000000\nEC8C8C8C4C4700001C7C7C7C3C3E3F1F\nEC8C8C8C981830601C7C7C7C78F8F0E0\n00000000000000000000000000000000\n00000000000000000000000000000000\nFF80800003232323007F7FFF3F1F1F1F\nFE0000000323232301FFFFFF3F1F1F1F\nC0C0C0C000000000C0C0C0C000000000\n00000000000000000000100000000000\n00000000000004000000000000000400\n00483030480000000078484878000000\n8C8C8C86030000017C7C7C7EFC7F7F1F\nEC8C8C8C0C0C0C0C1C7C7C7CFCFCFCFC\n878080808C8C8C0C787F7F7F7C7C7CFC\n8C0C0C0C8C8C8C8C7CFCFCFC7C7C7C7C\n8C8C8C8C878080007C7C7C7C787F7FFF\n00000000EC0C0C1C000000001CFCFCFC\n2046468C8C8C8C0C1F3F3F7C7C7C7CFC\n3018180C0C8C8C0CF0F8F8FCFC7C7CFC\n0808080808080800070707070707070F\nC0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0\n23232323E18080001F1F1F1F1E7F7FFF\n23232323E00000001F1F1F1F1FFFFFFF\n00000000C0C0C0C000000000C0C0C0C0\n0000000000000000FFFFFFFFFFFFFFFF\n00007EC3C37E0000FFFFFFFCFC81FFFF\n00000000000000000000000000000000\n000000000000082200000000001876DF\n00000000000000000000000000000000\nB0988C878280808070787C7E7D7F7F7F\n2C4C8C0C0C0C0C0C1C3C7CFCFCFCFCFC\nFF8080808C8C8680007F7F7F7C7C797F\nEC0C0C0C0000C0C01CFCFCFC0000C0C0\n8C8C8C07020000007C7C7CFE7D3F1F0F\n8C8C8C0C183060C07C7C7CFCF8F0E0C0\nFF8080808C8C8C8D007F7F7F7D7C7C7C\n6018180C0C0C0C0CE0F8F8FCFCFCFCFC\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0000000038443800FFFFFFFFFFFBC7FF\n00001C0E00000000FFFFFFF1FFFFFFFF\n000001040102164C0103060B1E3D69B3\n0000000000000000FFFFFFFFFFFFFFFF\n00804060B010280480C0E0F0F8FCFEFF\n898C8C8C8C8C8C0C7F7C7C7C7C7C7CFC\n8C8C8C8C8C8C8C0C7C7C7C7C7C7C7CFC\n80808C8C878080007F7F7C7C787F7FFF\nC0C00000EC0C0C0CC0C000001CFCFCFC\n8C8C8C87020000007C7C7C7EFD7F7F1F\n8C8C8C0C0C1818607C7C7CFCFCF8F8E0\n868080808C8C8C0C797F7F7F7F7D7CFC\n18186030180C0C0CF8F8E0F0F8FCFCFC\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00181818180018000000000000000000\n006C6C24000000000000000000000000\n00247E24247E24000000000000000000\n00083E380E3E08000000000000000000\n00626408102646000000000000000000\n001C34386E643A000000000000000000\n00181830000000000000000000000000\n000C183030180C000000000000000000\n0030180C0C1830000000000000000000\n000024187E1824000000000000000000\n000018187E1818000000000000000000\n00000000181830000000000000000000\n000000007E0000000000000000000000\n00000000001818000000000000000000\n00060C18306040000000000000000000\n003C666E76663C000000000000000000\n0018381818187E000000000000000000\n003C660C18307E000000000000000000\n003C660C06663C000000000000000000\n0066667E060606000000000000000000\n007E607C06067C000000000000000000\n001C307C66663C000000000000000000\n007E060C183030000000000000000000\n003C663C66663C000000000000000000\n003C663E06663C000000000000000000\n00000018001800000000000000000000\n00000018001830000000000000000000\n00000C1830180C000000000000000000\n0000007E007E00000000000000000000\n000030180C1830000000000000000000\n003C660C180018000000000000000000\n003C666E6E603C000000000000000000\n00183C667E6666000000000000000000\n007C667C66667C000000000000000000\n003C666060663C000000000000000000\n00786C66666C78000000000000000000\n007E607860607E000000000000000000\n007E6078606060000000000000000000\n003C606E66663C000000000000000000\n0066667E666666000000000000000000\n003C181818183C000000000000000000\n001E060606663C000000000000000000\n00666C78786C66000000000000000000\n0060606060607E000000000000000000\n0042667E7E6666000000000000000000\n0066767E6E6666000000000000000000\n003C666666663C000000000000000000\n007C667C606060000000000000000000\n003C66666A6C3E000000000000000000\n007C667C786C66000000000000000000\n003E603C06067C000000000000000000\n007E1818181818000000000000000000\n0066666666663C000000000000000000\n00666666663C18000000000000000000\n0066667E7E6642000000000000000000\n00663C183C6666000000000000000000\n0066663C181818000000000000000000\n007E0C1830607E000000000000000000\n003C303030303C000000000000000000\n006030180C0602000000000000000000\n003C0C0C0C0C3C000000000000000000\n00183C66000000000000000000000000\n0000000000007E000000000000000000\n\n#3:MAIN BG\n00002020000000000000002100210021\n00210000002100210021000000000000\n00000021002100210021002100000021\n00210000000000210021002100210021\n00210021000000000000002100210000\n00210000002100210021000000000000\n00000021002100210000002100000021\n00000021000000000021002100210021\n00210021002100000000000000210021\n00210021002100210021000000210000\n00210021002100000021002100000021\n00210021002100000000002100000021\n00000021000000210000000000000000\n00000000002100210021000000000021\n00000021000000000021002100000000\n00000000000000000021002100210000\n00210021000000000000002100210021\n00210021000000210021002100000021\n00210021000000210021002100000021\n00210021002100000021002100210000\n00000000000000000000002100210021\n00210000002100210021002100210021\n00210000000000210021002100000021\n00210021002100000000002100210021\n00000000002100210021000000000021\n00210021000000000021002100210021\n00000000002100210021002100000021\n00210021002100210000002100210021\n00210021002100210021002100210021\n00210021002100216201630164010021\n00210021000000210021002100210021\n00006201630164010021002100210021\n00210001000100016201630164010001\n62010001640163017201730174010001\n00016201630164010021002100010001\n63017201730174016301002100216201\n63016401630163017201730174016301\n63016301720173017301730173017401\n63017201730174016301630163017201\n73017301730173017301740163017201\n73017401600160016001600160016001\n60016001710160017301730173016001\n60016001710160016001600160016001\n71016001600160016001600160016001\n60016001700160016001710170017101\n70016001710160017001600170017101\n70016001600171016001600160017101\n60016001710160016001710160016001\n71016001600160016001710160016001\n60016001600160016001600160016001\n60017001610160016001710170016001\n60016001700160016001600160016001\n60016001700171016001600160017001\n60016101700171016001600170016001\n60016001700160017101600160016001\n61016001600170016001710170017101\n70017101600160017001600160016001\n60017001600160016001610160017001\n60016101600160016001700160017101\n60016001600161016001700160016001\n60016101700160016001610160017001\n60016001600170016001600160017101\n70017101600171017001710170016001\n70017101700160016001600161016001\n70017101400041000000000000000000\n51000000000000005000000000000000\n50000000000000000000000000000000\n00000000000000000000500000000000\n40004100500051000000400041000000\n00000000000000000000510000000000\n00005100000000004000410000000000\n00004000410000000000000000000000\n50004200000051000000500051000000\n50000000420041000000000040004100\n00000000510000005000510000000000\n00005000510000000000400041000000\n00000000000000000000000051000000\n51000000500051005000000050005100\n00005100000000000000000000000000\n00000000000000005100500051000000\n51000000000040004100000000000000\n00000000000000000000000000000000\n00004200000041000000000000005100\n40004100000000005201000000000000\n00000000000050005100500000004000\n50000000000040004100410000000000\n00000000510000000000500000000000\n50005100000000000000400041000000\n00000000000000005000000000005000\n51000000000050005100000000004000\n50000000500000005100000000005000\n00000000000051000000500051000000\n00000000000000005000410000000000\n00004000410000000000000000005000\n51000000000000000000000051000000\n00005100000050000000000000004000\n41000000500000005000420000005100\n62010001640100005201510000005100\n50005100000040004100000000004200\n00000000000000000000510000005000\n51000000510000000000000062016201\n00010001000100005100000000000000\n00000000000050005100000051000000\n50000000000000005100000051000000\n00000000400062016201620100010001\n00010001500000000000000062220002\n64225100000051000000000000000000\n00000000400041000000000000000000\n00000000500000010001000100010001\n41000000000000010001000100020002\n64020001000100010001000100010001\n00010001000100010001000100010001\n00010000000000000000000000000001\n00210020002000220002002200226322\n00220002000000000000000100010001\n00010001000000000000000100010001\n00010000000000006221632264220020\n00000002000262020002002272227322\n74220022000200016222632264220000\n00000000000000000000000062226322\n64010000000000027222732274220020\n00210022000200226322722270227322\n73227422002200027222732274226322\n63226402000062026322632272227322\n74220022002272227322712273227422\n00220002002272227322732273226122\n73227122742272227322732273227322\n73227422002072227322732271227322\n73227422\n\n#4:OVERLAY BG\n00001410000000000000AC20AF20B720\nB220A520B32000000000000000000000\n00000000000000000000000043204420\n45204620472048204520462049204A20\n49204A20000000000000000000000000\n00000000532054205520562057205820\n5520562059205A205B205C2000000000\n00000000000000000000000000000000\n00000000000200020002000200000000\n00000000000000000000000000000000\n00000000000000000000000200024D22\n4E224F22000000000000000000000000\n00000000000000000000000000000000\n0000000000025D225E225F2200000000\n00000000000000000000000000000000\n00000000000000000000000000020000\n00000000000000000000000000000000\n00000000000000000000000043254425\n45254625652566256725682580254525\n462547254725672568256B256C250000\n00000000532554255525562575257625\n77257825002579257A2569256A257725\n78257B257C2500000000000000000005\n00050000000000000000000000000000\n00000005000500250025000500050005\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000\n\n#14:SFX\n5801505019FC00005801506019FB0000\n4801004F19FF00007806007F01FE0000\n780A04AF19FD00007805027F19FD0000\n0800505012FF00000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n296FE6306F00356F002A6F00316F0036\n6F002C6F00336F00386F00FF00000000\nE0000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n\n#15:MAIN SOUND\n230030AA0004A0002800606019000000\n22006060000250002800303019FE0000\n38002020000000003800606000000000\n21004068120B30002400418A10372000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n800103180002041A000103180002041A\n000116180002171A000116180082171A\n85064040050B4040090A4040090C4040\n05064040050B40400D0E40400D0F4040\n11104040111040401314404013144040\n13154040139540404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n87884040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n193F00000000193F00000000315F0019\n3F00000000193F00000000554500193F\n00000000315F00000000000000000000\n193F00000000193F00000000315F0019\n3F00000000193F00000000000000193F\n00554500315F00000000554500554800\n1D0F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n1B0F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n190F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n1B0F0000000000000000000000000000\n00000000000000000000000000000000\n00000000180F00000000000000000000\n356F00000000356F00000000386F0000\n0000356F00000000000000000000386F\n00000000FF0000000000356F00000000\n3C6F000000000000000000003A6F0000\n0000386F000000000000000000003A6F\n00000000000000FF0000000000000000\n3D6F000000000000000000003C6F0000\n00003A6F00000000FF0000000000356F\n00000000000000000000FF0000000000\n386F00000000386F000000003A6F0000\n0000386F00000000000000000000376F\n00000000000000000000FF0000000000\n1B2FE81B2F000000001B2F001C2F0000\n00001B2F001B2F00000000202F001B2F\n001B2F001E2F001B2F001C2F001B2F00\n1B2F001B2F000000001B2F001C2F0000\n00001B2F001B2F00000000202F001B2F\n001B2F001E2F001B2F001C2F001B2F00\n337800000000000000FF000000000000\n00002E7800000000000000FF00000000\n00000000337800000000000000000000\n31780000000030780000000000000000\n00002E7800000000000000FF00000000\n000000002C7800000000000000000000\n190F0000000000000000000000000000\n0000000000000000FF00000000000000\n00000000000000000000000000000000\n190F0000000000000000000000000000\n0000000000000000FF00000000000000\n00000000000000000000000000000000\n4278003F78003A78003678003378002E\n78002A7800277800FF00000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n1E2F001E2F001E2F00000000192F0000\n0000192F001E2F000000001F2F000000\n001F2F000000001F2F000000001F2F00\n1E2F001E2F001E2F00000000192F0000\n0000192F001E2F000000001F2F000000\n001F2F000000001F2F001A2F001C2F00\n36780000000000000037780000000000\n00003678000000000000003778000000\n00000000367800000000FF0000000000\n2A78000000000000002B780000000000\n00002C78000000000000002D78000000\n000000002E78000000002F7800000000\n2E7800000000000000FF000000000000\n00002A7800000000000000FF00000000\n00000000257800000000000000000000\n277800000000000000000000FF000000\n00000000002C78000000000000002D78\n00000000317800000000FF0000000000\n36780000000000000037780000000000\n00003678000000000000003778000000\n00000000367800000000FF0000000000\n2A78000000000000002B780000000000\n00003078000000000000002F78000000\n000000002E78000000002D7800000000\n1E2F00000000192F000000001E2F0000\n0000192F001C2F000000001C2F001B2F\n00000000192F00000000172F00000000\n152F00152F00000000172F00172F0000\n0000192F00192F000000001C2F001C2F\n000000001B2F00000000192F00000000\n36780000000000000038780000000000\n0000397800000000000000FF00000000\n00000000000000000000000000000000\n34780000000000000033780000000000\n00003178000000000000002F78000000\n00000000317800000000FF0000000000\n36780000000000000038780000000000\n0000397800000000000000000000FF00\n00000000000000000000000000000000\n2D78000000000000002F780000000000\n00003178000000000000003478000000\n00000000317800000000FF0000000000\n33780000000000000000000000000000\n0000000000000000000000FF00000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n1B2F001B2F000000001B2F001C2F0000\n00001B2F001B2F00000000202F001B2F\n000000001C2F000000001C2F00000000\n1B2F001B2F00000000162F00162F0000\n0000172F00172F00000000122F00122F\n00000000122F00000000122F00000000\n1B2F001B2F000000000000001B2F0000\n00000000000000001B2F000000000000\n000000001B2F00000000000000000000\n122F00122F000000000000000F2F0000\n00000000000000001B2F000F2F000000\n000000001B2F000F2F00000000000000\n1B0FD000000000000000000000000000\n0000000000000000120F000000000000\n00000000000000000000000000000000\n190F0000000000000000000000000000\n0000000000000000140F000000000000\n00000000000000000000FF0000000000\n2A7800000000000000000000FF000000\n00000000000000002E78000000000000\n00000000FF0000000000000000000000\n2C780000000000000000000025780000\n00000000000000002778000000000000\n00000000FF0000000000000000000000\n2E7800000000000000000000FF000000\n00000000000000003178000000000000\n00000000FF0000000000000000000000\n3678000000000000000000003A780000\n00000000000000003378000000000000\n00000000FF0000000000000000000000\n416F00000000356F000000003C6F0000\n0000416F00000000000000FF00000000\n00000000000000000000000000000000\n436F00000000376F00000000446F0000\n0000466F00000000000000FF00000000\n00000000000000000000000000000000\n3D6F00000000316F00000000FF000000\n00003D6F00000000316F00000000FF00\n00000000000000000000000000000000\n436F00000000436F00000000446F0000\n0000436F000000003F6F000000000000\n00FF0000000000000000000000000000\n2918012C1A02291C01291C022C1C0129\n1C02291A012C18020000000000002918\n012C1A02291C01291A022C1801000000\n271801301A02271C01271C02301C0127\n1C02271A013018020000000000002718\n01301A02271C01271A02301801000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n251801291A02251C01251C02291C0125\n1C02291A012518020000000000002518\n01291A02251C01251A02291801000000\n271801271A022C1C01271C022C1C012C\n1C02271A012C18020000000000002718\n012C1A02271C01271A022C1801000000\n\n"
  },
  {
    "path": "platform/GameShell/home/cpi/games/LowResNX/Star Scroller 1.2.nx",
    "content": "'TITLE:   STAR SCROLLER\n'AUTHORS:  TIMO KLOSS, DESBYC\n\nDIM GLOBAL STARX(127)\n\nFOR I=0 TO 127\n  STARX(I)=INT(RND*256)\nNEXT I\nBG 1\nBG FILL 0,0 TO 0,15 CHAR 3\n\nGLOBAL SX,T\n\nON RASTER CALL RASTERFX\n\nT$=\"WELCOME TO LOWRES NX! A VIRTUAL RETRO GAME CONSOLE,WHICH CAN BE PROGRAMMED IN THE CLASSIC BASIC LANGUAGE. HAVE FUN! ... STAR SCROLLER CODE AND GFX BY TIMO, MUSIC BY DESBYC. GREETINGS TO ALL LOWRES NX CODERS!                     \"\n\nCELL SIZE 0,1\nBG 0\nPAL 1\nPRIO 1\nP=1\nL=LEN(T$)\n\nMUSIC\n\nDO\n  IF SX MOD 16=0 THEN\n    P$=MID$(T$,P,1)\n    IF P$>=\"A\" AND P$<=\"Z\" THEN\n      N=ASC(P$)-ASC(\"A\")\n      C=(N\\8)*32+(N MOD 8)*2+64\n    ELSE\n      IF P$=\".\" THEN C=164\n      IF P$=\",\" THEN C=166\n      IF P$=\"!\" THEN C=168\n      IF P$=\"?\" THEN C=170\n      IF P$=\" \" THEN C=174\n    END IF\n    CELL SX/16+10,0,C\n    P=(P MOD L)+1\n  END IF\n \n  SCROLL 0,SX,SIN(T*0.02)*32-56\n \n  P2=PEEK($FF5B)\n \n  FOR I=0 TO 31\n    X1=SIN(T*0.02+I*PI/16)\n    Y1=COS(T*0.03+I*PI/16)\n    X2=COS(T*0.045+I*0.09)\n    Y2=SIN(T*0.035+I*0.1)\n    X3=SIN(T*0.025+I*0.12)\n    Y3=COS(T*0.015+I*0.13)\n  \n    Z=1+P2/1536\n    X=(X1*X2*60+X3*32)*Z\n    Y=(Y1*Y2*60+Y3*32)*Z\n    SPRITE I PAL 2+(I/4) MOD 2\n    SPRITE I,X+76,Y+60,2\n  NEXT I\n \n  T=T+1\n  SX=SX+1\n  WAIT VBL\nLOOP\n\nSUB RASTERFX\n  F=0.5\n  D=((RASTER MOD 4)+1)\n  SCROLL 1,TIMER*F/D+STARX(RASTER),0\nEND SUB\n\n\n#1:MAIN PALETTES\n001B070205383420002F0A05003B2211\n00000000000000000000000000000000\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n00000000000000000000000000000000\n2402B1310183463C3C7ECFCFFFFF7E3C\n80008080800080800080808000808080\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0F3F7046C9909090000F3F3970606060\n00C0E020309090900000C0C0E0606060\nFFFF809F90909F9F007F7F606060607F\n00C0E020B09020400000C0C06060C080\n0F3F7046C9909090000F3F3970606060\n00C0E02030F000000000C0C0E0000000\nFFFF809E91909090007F7F6160606060\n00C0E020309090900000C0C0E0606060\nFFFF809F90909F9F007F7F606060607E\nF0F010F00000000000E0E00000000000\nFFFF809F90909F9F007F7F606060607E\nF0F010F00000000000E0E00000000000\n0F3F7046C9909093000F3F3970606060\n00C0E02030F000F00000C0C0E0000000\nF0F0909090909F9F006060606060607F\nF0F090909090909000606060606060E0\n9F9F809F909090F0607F7F6060606000\n90901090909090F060E0E06060606000\n9F9090909F9F80FF60606060607F7F00\n20909090A020C000C0606060C0C00000\n909090994F46300F60606070393F0F00\n0000F0F02020C000000000E0C0C00000\n909090919F9E80FF60606060617F7F00\n909090902020C000606060E0C0C00000\n819F90909F9F80FF7E606060607F7F00\n00000000F0F010F00000000000E0E000\n819F9090909090F07E60606060606000\n00000000000000000000000000000000\n939390994F46300F61606070393F0F00\nF09090902020C000E06060E0C0C00000\n809F9090909090F07F60606060606000\n10909090909090F0E060606060606000\n3F3F203909090909001F1F0606060606\nC0C040C0000000000080800000000000\nFFFF80FF00000000007F7F0000000000\nF0F010909090909000E0E06060606060\nF0F0909193969C99006060606163677E\nF0F0909020408000006060E0C0800000\nF0F09090909090900060606060606060\n00000000000000000000000000000000\nE0F0998F86909996006070797F6F6660\n70F09010109090900060E0E0E0606060\nF0F88C86939994920070787C6E676361\nF0F0909090909010006060606060E0E0\n0F3F7046C9909090000F3F3970606060\n00C0E020309090900000C0C0E0606060\nFFFF809F90909F9F007F7F606060607F\n00C0E020B090A0200000C0C06060C0C0\n090909093939203F06060606061F1F00\n00000000C0C040C00000000000808000\n00000001FFFE80FF00000000017F7F00\n909090902020C000606060E0C0C00000\n81999492919090F07E67636160606000\n0080C060309090F0000080C0E0606000\n909090909F9F80FF60606060607F7F00\n00000000F0F010F00000000000E0E000\n90909090909090F06060606060606000\n90909090909090F06060606060606000\n91909090909090F06060606060606000\n10909090909090F0E060606060606000\n909090994F46300F60606070393F0F00\n909090902020C000606060E0C0C00000\n809F9090909090F07F60606060606000\nC0000000000000000000000000000000\n0F3F7046C9909090000F3F3970606060\n00C0E020309090900000C0C0E0606060\nFFFF809F90909F9F007F7F606060607F\n00C0E020B090A0200000C0C06060C0C0\n0F3F7047C8884F67000F3F387070381F\n00C0E030F00000C00000C0E000000000\nFFFF80F909090909007F7F0606060606\nF0F010F00000000000E0E00000000000\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F059492F26101000603030191F0F0F\nF0F0A020404080800060C0C080800000\n909090984F47300F60606070383F0F00\n90909090A03010F060606060C0E0E000\n80989492919090F07F67636160606000\n4080C060309090F0800080C0E0606000\n1E01F1F15F4E300F01000060313F0F00\nE02030102020C000C0C0E0E0C0C00000\n090909090909090F0606060606060600\n00000000000000000000000000000000\n909090994F46300F60606070393F0F00\n909090902020C000606060E0C0C00000\n905949292F16100F60303010190F0F00\n90A020404080800060C0C08080000000\n969F9990868990E060666F7F79706000\n9090909010109070606060E0E0E06000\n101030266949D0F00F0F1F1930306000\n8080C0406020B0F000008080C0C06000\nF0F059492F26101000603030191F0F0F\nF0F0A020404080800060C0C080800000\nFFFF80FF0103060C007F7F0000010307\nF0F010909020408000E0E060E0C08000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\nF8F88888888888880070707070707070\n00000000000000000000000000000000\n0F3F76C9F0010306000F397000000103\n00C0E020B09020400000C0C060E0C080\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n090909090909090F0606060606060600\n00000000000000000000000000000000\n193264C89F9F80FF0E1C3870607F7F00\n00000000F0F010F00000000000E0E000\n00000070F88888700000000070707000\n00000000000000000000000000000000\n000000787850D0E00000000030206000\n00000000000000000000000000000000\n50700070F88888702000000070707000\n00000000000000000000000000000000\n060D0F00060F09060306000000060600\n80000000000000000000000000000000\n\n#15:MAIN SOUND\n6801F03200017000680200F508026000\n2800303019FE00007801F04F0AFF0000\n7801F00F02FF00000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n00404007000103400002030700040340\n80040306000403060004030600010306\n00020306000440400000030300000305\n00000305000403060000030300814007\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n170F44190F00160F44190F00190F0027\n0F00190F44250F00170F001A0F001D0F\n44190F00190F00250F00190F00190F00\n170F001E0F44160F00190F00190F001B\n0F00190F00190F00170F001A0F00250F\n47190F00190F00190F00190F00190F00\n311F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n3D1F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n192F00000000000000000000192F0000\n0000000000000000192F00192F000000\n00000000192F00000000000000000000\n192F00000000000000000000192F0000\n0000000000000000192F000000000000\n00000000192F00192F00192F00192F00\n461F00471F003B1F003A1F0000000000\n0000000000000000441F00451F00391F\n00381F00000000000000000000000000\n3F1F00401F00341F00331F0000000000\n00000000000000003D1F003E1F00321F\n00000000411F00000000000000000000\n5F4F005F4F005F4F005F4F005F4F005F\n4F005F4F005F4F005F4F005F4F005F4F\n005F4F005F4F005F4F005F4F005F4F00\n5F4F005F4F005F4F005F4F005F4F005F\n4F005F4F005F4F005F4F005F4F005F4F\n005F4F005F4F005F4F005F4F005F4F00\n5F4F005F4F005F4F005F4F003D3F005F\n4F005F4F005F4F005F4F005F4F005F4F\n005F4F003D3F005F4F005F4F005F4F00\n5F4F005F4F005F4F005F4F003D3F005F\n4F005F4F005F4F005F4F005F4F005F4F\n005F4F003D3F005F4F003D3F003D3F00\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000323F00000000323F00323F00\n\n"
  },
  {
    "path": "platform/LibRetro/Makefile",
    "content": "DEBUG=0\nPSS_STYLE=1\nEXTERNAL_ZLIB=0\nHAVE_GRIFFIN=1\nSTATIC_LINKING=0\nENDIANNESS_DEFINES=\n\nSPACE :=\nSPACE := $(SPACE) $(SPACE)\nBACKSLASH :=\nBACKSLASH := \\$(BACKSLASH)\nfilter_out1 = $(filter-out $(firstword $1),$1)\nfilter_out2 = $(call filter_out1,$(call filter_out1,$1))\nunixpath = $(subst \\,/,$1)\nunixcygpath = /$(subst :,,$(call unixpath,$1))\n\nifeq ($(platform),)\n\tplatform = unix\n\tifeq ($(shell uname -a),)\n\t\tplatform = win\n\telse ifneq ($(findstring MINGW,$(shell uname -a)),)\n\t\tplatform = win\n\telse ifneq ($(findstring Darwin,$(shell uname -a)),)\n\t\tplatform = osx\n\t\tarch = intel\n\t\tifeq ($(shell uname -p),powerpc)\n\t\t\tarch = ppc\n\t\tendif\n\telse ifneq ($(findstring win,$(shell uname -a)),)\n\t\tplatform = win\n\tendif\nendif\n\n# system platform\nsystem_platform = unix\nifeq ($(shell uname -a),)\n\tEXE_EXT = .exe\n\tsystem_platform = win\nelse ifneq ($(findstring Darwin,$(shell uname -a)),)\n\tsystem_platform = osx\n\tarch = intel\n\tifeq ($(shell uname -p),powerpc)\n\t\tarch = ppc\n\tendif\nelse ifneq ($(findstring MINGW,$(shell uname -a)),)\n\tsystem_platform = win\nendif\n\n# Replace 'sample' with the name of the core\nTARGET_NAME := lowresnx\nGIT_VERSION := \" $(shell git rev-parse --short HEAD || echo unknown)\"\nifneq ($(GIT_VERSION),\" unknown\")\n\tCFLAGS += -DGIT_VERSION=\\\"$(GIT_VERSION)\\\"\n\tCXXFLAGS += -DGIT_VERSION=\\\"$(GIT_VERSION)\\\"\nendif\n\nifneq (,$(findstring msvc,$(platform)))\nLIBM :=\nelse\nLIBM := -lm\nendif\nLIBS :=\n\nCORE_DIR := .\n\n# Unix\nifeq ($(platform), unix)\n\tTARGET := $(TARGET_NAME)_libretro.so\n\tfpic := -fPIC\nifneq ($(findstring SunOS,$(shell uname -a)),)\n\tCC = gcc\n\tSHARED := -shared -z defs\nelse\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined\nendif\n\nelse ifeq ($(platform), linux-portable)\n\tTARGET := $(TARGET_NAME)_libretro.so\n\tfpic := -fPIC -nostdlib\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T\n\tLIBM :=\n\n# Classic Platforms ####################\n# Platform affix = classic_<ISA>_<µARCH>\n# Help at https://modmyclassic.com/comp\n\n# (armv7 a7, hard point, neon based) ### \n# NESC, SNESC, C64 mini \nelse ifeq ($(platform), classic_armv7_a7)\n\tCC = gcc\n\tTARGET := $(TARGET_NAME)_libretro.so\n\tfpic := -fPIC\n  LDFLAGS += $(fpic) -shared -Wl,--version-script=link.T\n\tCFLAGS += -Ofast \\\n\t -fwhole-program -fuse-linker-plugin \\\n\t-fdata-sections -ffunction-sections -Wl,--gc-sections \\\n\t-fno-stack-protector -fno-ident -fomit-frame-pointer \\\n\t-falign-functions=1 -falign-jumps=1 -falign-loops=1 \\\n\t-fno-unwind-tables -fno-asynchronous-unwind-tables -fno-unroll-loops \\\n\t-fmerge-all-constants -fno-math-errno \\\n\t-marm -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard\n\tCXXFLAGS += $(CFLAGS)\n\tCPPFLAGS += $(CFLAGS)\n\tASFLAGS += $(CFLAGS)\n\tHAVE_NEON = 1\n\tARCH = arm\n\tBUILTIN_GPU = neon\n\tUSE_DYNAREC = 1\n  CPU_ARCH := arm\n  ARM = 1\n\tifeq ($(shell echo `$(CC) -dumpversion` \"< 4.9\" | bc -l), 1)\n\t  CFLAGS += -march=armv7-a\n\telse\n\t  CFLAGS += -march=armv7ve\n\t  # If gcc is 5.0 or later\n\t  ifeq ($(shell echo `$(CC) -dumpversion` \">= 5\" | bc -l), 1)\n\t    LDFLAGS += -static-libgcc -static-libstdc++\n\t  endif\n\tendif\n#######################################\n\n# OS X\nelse ifeq ($(platform), osx)\n\tTARGET := $(TARGET_NAME)_libretro.dylib\n\tfpic := -fPIC\n\tSHARED := -dynamiclib\n\tifeq ($(arch),ppc)\n\t\tENDIANNESS_DEFINES += -DMSB_FIRST\n\t\tCFLAGS += -std=gnu99\n\tendif\n\tOSXVER = `sw_vers -productVersion | cut -d. -f 2`\n\tOSX_LT_MAVERICKS = `(( $(OSXVER) <= 9)) && echo \"YES\"`\n   ifeq ($(OSX_LT_MAVERICKS),YES)\n\tfpic += -mmacosx-version-min=10.1\n   endif\n\n   ifeq ($(CROSS_COMPILE),1)\n\t\tTARGET_RULE   = -target $(LIBRETRO_APPLE_PLATFORM) -isysroot $(LIBRETRO_APPLE_ISYSROOT)\n\t\tCFLAGS   += $(TARGET_RULE)\n\t\tCPPFLAGS += $(TARGET_RULE)\n\t\tCXXFLAGS += $(TARGET_RULE)\n\t\tLDFLAGS  += $(TARGET_RULE)\n   endif\n\n\tCFLAGS  += $(ARCHFLAGS)\n\tCXXFLAGS  += $(ARCHFLAGS)\n\tLDFLAGS += $(ARCHFLAGS)\n\n# iOS\nelse ifneq (,$(findstring ios,$(platform)))\n\n\tTARGET := $(TARGET_NAME)_libretro_ios.dylib\n\tfpic := -fPIC\n\tSHARED := -dynamiclib\n        MINVERSION := \n\tifeq ($(IOSSDK),)\n\t  IOSSDK := $(shell xcodebuild -version -sdk iphoneos Path)\n\tendif\n\tifeq ($(platform),ios-arm64)\n\t  CC = cc -arch arm64 -isysroot $(IOSSDK)\n\telse\n\t  CC = cc -arch armv7 -isysroot $(IOSSDK)\n\tendif\n\tCFLAGS   += -DIOS\n\tCXXFLAGS += -DIOS\nifeq ($(platform),$(filter $(platform),ios9 ios-arm64))\n\tMINVERSION = -miphoneos-version-min=8.0\nelse\n\tMINVERSION = -miphoneos-version-min=5.0\nendif\n\tCFLAGS   += $(MINVERSION)\n\tCXXFLAGS += $(MINVERSION)\n\tLDFLAGS  += $(MINVERSION)\n\nelse ifeq ($(platform), tvos-arm64)\n        TARGET := $(TARGET_NAME)_libretro_tvos.dylib\n        fpic := -fPIC\n        SHARED := -dynamiclib\n        CFLAGS += -DIOS\n        CXXFLAGS += -DIOS\n\nifeq ($(IOSSDK),)\n        IOSSDK := $(shell xcodebuild -version -sdk appletvos Path)\nendif\n\n        CC  = cc -arch arm64 -isysroot $(IOSSDK)\n        CXX = c++ -arch arm64 -isysroot $(IOSSDK)\n\n# Theos iOS\nelse ifeq ($(platform), theos_ios)\n\tDEPLOYMENT_IOSVERSION = 5.0\n\tTARGET = iphone:latest:$(DEPLOYMENT_IOSVERSION)\n\tARCHS = armv7 armv7s\n\tTARGET_IPHONEOS_DEPLOYMENT_VERSION=$(DEPLOYMENT_IOSVERSION)\n\tTHEOS_BUILD_DIR := objs\n\tinclude $(THEOS)/makefiles/common.mk\n\tLIBRARY_NAME = $(TARGET_NAME)_libretro_ios\n\n# QNX\nelse ifeq ($(platform), qnx)\n\tTARGET := $(TARGET_NAME)_libretro_qnx.so\n\tfpic := -fPIC\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined\n\tCC = qcc -Vgcc_ntoarmv7le\n\tAR = qcc -Vgcc_ntoarmv7le\n\tPLATFORM_DEFINES := -D__BLACKBERRY_QNX__ -marm -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp\n\n# PS3\nelse ifeq ($(platform), ps3)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe\n\tAR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe\n\tPLATFORM_DEFINES := -D__CELLOS_LV2\n\tENDIANNESS_DEFINES += -DMSB_FIRST\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# sncps3\nelse ifeq ($(platform), sncps3)\n\tTARGET := $(TARGET_NAME)_libretro_ps3.a\n\tCC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe\n\tAR = $(CELL_SDK)/host-win32/sn/bin/ps3snarl.exe\n\tPLATFORM_DEFINES := -D__CELLOS_LV2\n\tENDIANNESS_DEFINES += -DMSB_FIRST\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\nelse ifeq ($(platform), ps2)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = mips64r5900el-ps2-elf-gcc$(EXE_EXT)\n\tCXX = mips64r5900el-ps2-elf-g++$(EXE_EXT)\n\tAR = mips64r5900el-ps2-elf-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -DPS2 -G0 -fomit-frame-pointer -ffast-math\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# PSP\nelse ifeq ($(platform), psp1)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = psp-gcc$(EXE_EXT)\n\tAR = psp-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -DPSP -G0\n\tPLATFORM_DEFINES += -march=allegrex -mfp32 -mgp32 -mlong32 -mabi=eabi\n\tPLATFORM_DEFINES += -fomit-frame-pointer -fstrict-aliasing\n\tPLATFORM_DEFINES += -falign-functions=32 -falign-loops -falign-labels -falign-jumps\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# Vita\nelse ifeq ($(platform), vita)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = arm-vita-eabi-gcc$(EXE_EXT)\n\tAR = arm-vita-eabi-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -DVITA\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# DOS\nelse ifeq ($(platform), dos)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = i586-pc-msdosdjgpp-gcc\n\tCXX = i586-pc-msdosdjgpp-g++\n\tAR = i586-pc-msdosdjgpp-ar\n\tCFLAGS += -march=i386\n\tCXXFLAGS += -march=i386\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# CTR(3DS)\nelse ifeq ($(platform), ctr)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = $(DEVKITARM)/bin/arm-none-eabi-gcc$(EXE_EXT)\n\tAR = $(DEVKITARM)/bin/arm-none-eabi-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -DARM11 -D_3DS\n\tCFLAGS += -march=armv6k -mtune=mpcore -mfloat-abi=hard\n\tCFLAGS += -Wall -mword-relocations\n\tCFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math\n\tCXXFLAGS += -march=armv6k -mtune=mpcore -mfloat-abi=hard\n\tCXXFLAGS += -Wall -mword-relocations\n\tCXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# Nintendo Switch (libnx)\nelse ifeq ($(platform), libnx)\n\tinclude $(DEVKITPRO)/libnx/switch_rules\n\tPORTLIBS := $(PORTLIBS_PATH)/switch\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tDEFINES := -DSWITCH=1 -D__SWITCH__ -DARM\n\tCFLAGS += $(DEFINES) -fPIE -I$(PORTLIBS)/include/ -I$(LIBNX)/include/ -ffunction-sections -fdata-sections -ftls-model=local-exec -specs=$(LIBNX)/switch.specs\n\tCFLAGS += -march=armv8-a -mtune=cortex-a57 -mtp=soft -mcpu=cortex-a57+crc+fp+simd -ffast-math\n\tCXXFLAGS += $(CFLAGS)\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# Raspberry Pi 1\nelse ifeq ($(platform), rpi1)\n\tTARGET := $(TARGET_NAME)_libretro.so\n\tfpic := -fPIC\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined\n\tCFLAGS += -DARM11\n\tCFLAGS += -marm -march=armv6j -mfpu=vfp -mfloat-abi=hard -funsafe-math-optimizations\n\tCFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math\n\tCXXFLAGS += -DARM11\n\tCXXFLAGS += -marm -march=armv6j -mfpu=vfp -mfloat-abi=hard -funsafe-math-optimizations\n\tCXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math\n\n# Raspberry Pi 2\nelse ifeq ($(platform), rpi2)\n\tTARGET := $(TARGET_NAME)_libretro.so\n\tfpic := -fPIC\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined\n\tCFLAGS += -DARM\n\tCFLAGS += -marm -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -funsafe-math-optimizations\n\tCFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math\n\tCXXFLAGS += -DARM\n\tCXXFLAGS += -marm -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -funsafe-math-optimizations\n\tCXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -ffast-math\n\n# Lightweight PS3 Homebrew SDK\nelse ifeq ($(platform), psl1ght)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = $(PS3DEV)/ppu/bin/ppu-gcc$(EXE_EXT)\n\tAR = $(PS3DEV)/ppu/bin/ppu-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -D__CELLOS_LV2\n\tENDIANNESS_DEFINES += -DMSB_FIRST\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# Xbox 360\nelse ifeq ($(platform), xenon)\n\tTARGET := $(TARGET_NAME)_libretro_xenon360.a\n\tCC = xenon-gcc$(EXE_EXT)\n\tAR = xenon-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -D__LIBXENON__\n\tENDIANNESS_DEFINES += -DMSB_FIRST\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# Nintendo Game Cube\nelse ifeq ($(platform), ngc)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)\n\tAR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -DGEKKO -DHW_DOL -mrvl -mcpu=750 -meabi -mhard-float\n\tPLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int\n\tENDIANNESS_DEFINES += -DMSB_FIRST\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# Nintendo Wii\nelse ifeq ($(platform), wii)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)\n\tAR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -DGEKKO -DHW_RVL -mrvl -mcpu=750 -meabi -mhard-float\n\tPLATFORM_DEFINES += -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int\n\tENDIANNESS_DEFINES += -DMSB_FIRST\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# Nintendo WiiU\nelse ifeq ($(platform), wiiu)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).a\n\tCC = $(DEVKITPPC)/bin/powerpc-eabi-gcc$(EXE_EXT)\n\tAR = $(DEVKITPPC)/bin/powerpc-eabi-ar$(EXE_EXT)\n\tPLATFORM_DEFINES := -DGEKKO -DHW_RVL -mcpu=750 -meabi -mhard-float\n\tPLATFORM_DEFINES += -ffunction-sections -fdata-sections -D__wiiu__ -D__wut__\n\tENDIANNESS_DEFINES += -DMSB_FIRST\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# Nintendo Switch (libtransistor)\nelse ifeq ($(platform), switch)\n\tEXT=a\n        TARGET := $(TARGET_NAME)_libretro_$(platform).$(EXT)\n        include $(LIBTRANSISTOR_HOME)/libtransistor.mk\n        STATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n\n# ARM\nelse ifneq (,$(findstring armv,$(platform)))\n\tTARGET := $(TARGET_NAME)_libretro.so\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined\n\tfpic := -fPIC\n\tifneq (,$(findstring cortexa5,$(platform)))\n\t\tPLATFORM_DEFINES += -marm -mcpu=cortex-a5\n\telse ifneq (,$(findstring cortexa8,$(platform)))\n\t\tPLATFORM_DEFINES += -marm -mcpu=cortex-a8\n\telse ifneq (,$(findstring cortexa9,$(platform)))\n\t\tPLATFORM_DEFINES += -marm -mcpu=cortex-a9\n\telse ifneq (,$(findstring cortexa15a7,$(platform)))\n\t\tPLATFORM_DEFINES += -marm -mcpu=cortex-a15.cortex-a7\n\telse\n\t\tPLATFORM_DEFINES += -marm\n\tendif\n\tifneq (,$(findstring softfloat,$(platform)))\n\t\tPLATFORM_DEFINES += -mfloat-abi=softfp\n\telse ifneq (,$(findstring hardfloat,$(platform)))\n\t\tPLATFORM_DEFINES += -mfloat-abi=hard\n\tendif\n\tPLATFORM_DEFINES += -DARM\n\n# emscripten\nelse ifeq ($(platform), emscripten)\n\tTARGET := $(TARGET_NAME)_libretro_$(platform).bc\n\tSTATIC_LINKING=1\n\tEXTERNAL_ZLIB=1\n\n# GCW0\nelse ifeq ($(platform), gcw0)\n\tTARGET := $(TARGET_NAME)_libretro.so\n\tCC = /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc\n\tCXX = /opt/gcw0-toolchain/usr/bin/mipsel-linux-g++\n\tAR = /opt/gcw0-toolchain/usr/bin/mipsel-linux-ar\n\tfpic := -fPIC\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined\n\tPLATFORM_DEFINES += -ffast-math -march=mips32 -mtune=mips32r2 -mhard-float\n\tCFLAGS += -std=gnu11\n\tEXTERNAL_ZLIB = 1\n# RS90\nelse ifeq ($(platform), rs90)\n   TARGET := $(TARGET_NAME)_libretro.so\n   CC = /opt/rs90-toolchain/usr/bin/mipsel-linux-gcc\n   CXX = /opt/rs90-toolchain/usr/bin/mipsel-linux-g++\n   AR = /opt/rs90-toolchain/usr/bin/mipsel-linux-ar\n   fpic := -fPIC\n   SHARED := -shared -Wl,-version-script=$(CORE_DIR)/link.T\n   PLATFORM_DEFINES := -DCC_RESAMPLER -DCC_RESAMPLER_NO_HIGHPASS\n   CFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32\n   CXXFLAGS += -fomit-frame-pointer -ffast-math -march=mips32 -mtune=mips32\n# RETROFW\nelse ifeq ($(platform), retrofw)\n\tEXT ?= so\n\tTARGET := $(TARGET_NAME)_libretro.$(EXT)\n\tCC = /opt/retrofw-toolchain/usr/bin/mipsel-linux-gcc\n\tAR = /opt/retrofw-toolchain/usr/bin/mipsel-linux-ar\n\tfpic := -fPIC\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,--no-undefined\n\tCFLAGS += -ffast-math -march=mips32 -mtune=mips32 -mhard-float\n\tCXXFLAGS += -ffast-math -march=mips32 -mtune=mips32 -mhard-float\n\tLIBS = -lm\n\n# Miyoo\nelse ifeq ($(platform), miyoo)\n\tTARGET := $(TARGET_NAME)_libretro.so\n\tCC = /opt/miyoo/usr/bin/arm-linux-gcc\n\tCXX = /opt/miyoo/usr/bin/arm-linux-g++\n\tAR = /opt/miyoo/usr/bin/arm-linux-ar\n\tfpic := -fPIC\n\tSHARED := -shared -Wl,--version-script=$(CORE_DIR)/link.T -Wl,-no-undefined\n\tPLATFORM_DEFINES += -ffast-math -mcpu=arm926ej-s\n\tEXTERNAL_ZLIB = 1\n\n# Windows MSVC 2017 all architectures\nelse ifneq (,$(findstring windows_msvc2017,$(platform)))\n\n\tPlatformSuffix = $(subst windows_msvc2017_,,$(platform))\n\tifneq (,$(findstring desktop,$(PlatformSuffix)))\n\t\tWinPartition = desktop\n\t\tMSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -FS\n\t\tLDFLAGS += -MANIFEST -LTCG:incremental -NXCOMPAT -DYNAMICBASE -DEBUG -OPT:REF -INCREMENTAL:NO -SUBSYSTEM:WINDOWS -MANIFESTUAC:\"level='asInvoker' uiAccess='false'\" -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1\n\t\tLIBS += kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib\n\telse ifneq (,$(findstring uwp,$(PlatformSuffix)))\n\t\tWinPartition = uwp\n\t\tMSVC2017CompileFlags = -DWINAPI_FAMILY=WINAPI_FAMILY_APP -DWINDLL -D_UNICODE -DUNICODE -DWRL_NO_DEFAULT_LIB -FS\n\t\tLDFLAGS += -APPCONTAINER -NXCOMPAT -DYNAMICBASE -MANIFEST:NO -LTCG -OPT:REF -SUBSYSTEM:CONSOLE -MANIFESTUAC:NO -OPT:ICF -ERRORREPORT:PROMPT -NOLOGO -TLBID:1 -DEBUG:FULL -WINMD:NO\n\t\tLIBS += WindowsApp.lib\n\tendif\n\n\tCFLAGS   += $(MSVC2017CompileFlags)\n\tCXXFLAGS += $(MSVC2017CompileFlags)\n\n\tTargetArchMoniker = $(subst $(WinPartition)_,,$(PlatformSuffix))\n\n\tCC  = cl.exe\n\tCXX = cl.exe\n\tLD = link.exe\n\n\treg_query = $(call filter_out2,$(subst $2,,$(shell reg query \"$2\" -v \"$1\" 2>nul)))\n\tfix_path = $(subst $(SPACE),\\ ,$(subst \\,/,$1))\n\n\tProgramFiles86w := $(shell cmd /c \"echo %PROGRAMFILES(x86)%\")\n\tProgramFiles86 := $(shell cygpath \"$(ProgramFiles86w)\")\n\n\tWindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0)\n\tWindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\\SOFTWARE\\Wow6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0)\n\tWindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0)\n\tWindowsSdkDir ?= $(call reg_query,InstallationFolder,HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0)\n\tWindowsSdkDir := $(WindowsSdkDir)\n\n\tWindowsSDKVersion ?= $(firstword $(foreach folder,$(subst $(subst \\,/,$(WindowsSdkDir)Include/),,$(wildcard $(call fix_path,$(WindowsSdkDir)Include\\*))),$(if $(wildcard $(call fix_path,$(WindowsSdkDir)Include/$(folder)/um/Windows.h)),$(folder),)))$(BACKSLASH)\n\tWindowsSDKVersion := $(WindowsSDKVersion)\n\n\tVsInstallBuildTools = $(ProgramFiles86)/Microsoft Visual Studio/2017/BuildTools\n\tVsInstallEnterprise = $(ProgramFiles86)/Microsoft Visual Studio/2017/Enterprise\n\tVsInstallProfessional = $(ProgramFiles86)/Microsoft Visual Studio/2017/Professional\n\tVsInstallCommunity = $(ProgramFiles86)/Microsoft Visual Studio/2017/Community\n\n\tVsInstallRoot ?= $(shell if [ -d \"$(VsInstallBuildTools)\" ]; then echo \"$(VsInstallBuildTools)\"; fi)\n\tifeq ($(VsInstallRoot), )\n\t\tVsInstallRoot = $(shell if [ -d \"$(VsInstallEnterprise)\" ]; then echo \"$(VsInstallEnterprise)\"; fi)\n\tendif\n\tifeq ($(VsInstallRoot), )\n\t\tVsInstallRoot = $(shell if [ -d \"$(VsInstallProfessional)\" ]; then echo \"$(VsInstallProfessional)\"; fi)\n\tendif\n\tifeq ($(VsInstallRoot), )\n\t\tVsInstallRoot = $(shell if [ -d \"$(VsInstallCommunity)\" ]; then echo \"$(VsInstallCommunity)\"; fi)\n\tendif\n\tVsInstallRoot := $(VsInstallRoot)\n\n\tVcCompilerToolsVer := $(shell cat \"$(VsInstallRoot)/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt\" | grep -o '[0-9\\.]*')\n\tVcCompilerToolsDir := $(VsInstallRoot)/VC/Tools/MSVC/$(VcCompilerToolsVer)\n\n\tWindowsSDKSharedIncludeDir := $(shell cygpath -w \"$(WindowsSdkDir)\\Include\\$(WindowsSDKVersion)\\shared\")\n\tWindowsSDKUCRTIncludeDir := $(shell cygpath -w \"$(WindowsSdkDir)\\Include\\$(WindowsSDKVersion)\\ucrt\")\n\tWindowsSDKUMIncludeDir := $(shell cygpath -w \"$(WindowsSdkDir)\\Include\\$(WindowsSDKVersion)\\um\")\n\tWindowsSDKUCRTLibDir := $(shell cygpath -w \"$(WindowsSdkDir)\\Lib\\$(WindowsSDKVersion)\\ucrt\\$(TargetArchMoniker)\")\n\tWindowsSDKUMLibDir := $(shell cygpath -w \"$(WindowsSdkDir)\\Lib\\$(WindowsSDKVersion)\\um\\$(TargetArchMoniker)\")\n\n\t# For some reason the HostX86 compiler doesn't like compiling for x64\n\t# (\"no such file\" opening a shared library), and vice-versa.\n\t# Work around it for now by using the strictly x86 compiler for x86, and x64 for x64.\n\t# NOTE: What about ARM?\n\tifneq (,$(findstring x64,$(TargetArchMoniker)))\n\t\tVCCompilerToolsBinDir := $(VcCompilerToolsDir)\\bin\\HostX64\n\telse\n\t\tVCCompilerToolsBinDir := $(VcCompilerToolsDir)\\bin\\HostX86\n\tendif\n\n\tPATH := $(shell IFS=$$'\\n'; cygpath \"$(VCCompilerToolsBinDir)/$(TargetArchMoniker)\"):$(PATH)\n\tPATH := $(PATH):$(shell IFS=$$'\\n'; cygpath \"$(VsInstallRoot)/Common7/IDE\")\n\tINCLUDE := $(shell IFS=$$'\\n'; cygpath -w \"$(VcCompilerToolsDir)/include\")\n\tLIB := $(shell IFS=$$'\\n'; cygpath -w \"$(VcCompilerToolsDir)/lib/$(TargetArchMoniker)\")\n\n\texport INCLUDE := $(INCLUDE);$(WindowsSDKSharedIncludeDir);$(WindowsSDKUCRTIncludeDir);$(WindowsSDKUMIncludeDir)\n\texport LIB := $(LIB);$(WindowsSDKUCRTLibDir);$(WindowsSDKUMLibDir)\n\tTARGET := $(TARGET_NAME)_libretro.dll\n\tPSS_STYLE :=2\n\tLDFLAGS += -DLL\n\n# Windows MSVC 2010 x64\nelse ifeq ($(platform), windows_msvc2010_x64)\n\tCC  = cl.exe\n\tCXX = cl.exe\n\nPATH := $(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../../VC/bin/amd64\"):$(PATH)\nPATH := $(PATH):$(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../IDE\")\nINCLUDE := $(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../../VC/include\")\nLIB := $(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../../VC/lib/amd64\")\nBIN := $(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../../VC/bin\")\n\nWindowsSdkDir := $(shell reg query \"HKLM\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.0A\" -v \"InstallationFolder\" | grep -o '[A-Z]:\\\\.*')lib/x64\nWindowsSdkDir ?= $(shell reg query \"HKLM\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.1A\" -v \"InstallationFolder\" | grep -o '[A-Z]:\\\\.*')lib/x64\n\nexport INCLUDE := $(INCLUDE)\nexport LIB := $(LIB);$(WindowsSdkDir)\nTARGET := $(TARGET_NAME)_libretro.dll\nPSS_STYLE :=2\nLDFLAGS += -DLL\n\n# Windows MSVC 2010 x86\nelse ifeq ($(platform), windows_msvc2010_x86)\n\tCC  = cl.exe\n\tCXX = cl.exe\n\nPATH := $(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../../VC/bin\"):$(PATH)\nPATH := $(PATH):$(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../IDE\")\nINCLUDE := $(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../../VC/include\")\nLIB := $(shell IFS=$$'\\n'; cygpath -w \"$(VS100COMNTOOLS)../../VC/lib\")\nBIN := $(shell IFS=$$'\\n'; cygpath \"$(VS100COMNTOOLS)../../VC/bin\")\n\nWindowsSdkDir := $(shell reg query \"HKLM\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.0A\" -v \"InstallationFolder\" | grep -o '[A-Z]:\\\\.*')lib\nWindowsSdkDir ?= $(shell reg query \"HKLM\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v7.1A\" -v \"InstallationFolder\" | grep -o '[A-Z]:\\\\.*')lib\n\nexport INCLUDE := $(INCLUDE)\nexport LIB := $(LIB);$(WindowsSdkDir)\nTARGET := $(TARGET_NAME)_libretro.dll\nPSS_STYLE :=2\nLDFLAGS += -DLL\n\n# Windows MSVC 2003 Xbox 1\nelse ifeq ($(platform), xbox1_msvc2003)\nTARGET := $(TARGET_NAME)_libretro_xdk1.lib\nCC  = CL.exe\nCXX  = CL.exe\nLD   = lib.exe\n\nexport INCLUDE := $(XDK)\\xbox\\include\nexport LIB := $(XDK)\\xbox\\lib\nPATH := $(call unixcygpath,$(XDK)/xbox/bin/vc71):$(PATH)\n\nPSS_STYLE :=2\nCFLAGS   += -D_XBOX -D_XBOX1\nCXXFLAGS += -D_XBOX -D_XBOX1\nSTATIC_LINKING=1\n\n# Windows MSVC 2010 Xbox 360\nelse ifeq ($(platform), xbox360_msvc2010)\nTARGET := $(TARGET_NAME)_libretro_xdk360.lib\nCC  = cl.exe\nCXX  = cl.exe\nLD   = lib.exe\n\nexport INCLUDE := $(XEDK)/include/xbox\nexport LIB := $(XEDK)/lib/xbox\nPATH := $(call unixcygpath,$(XEDK)/bin/win32):$(PATH)\n\nPSS_STYLE :=2\nCFLAGS   += -D_XBOX -D_XBOX360\nCXXFLAGS += -D_XBOX -D_XBOX360\nENDIANNESS_DEFINES += -DMSB_FIRST\nSTATIC_LINKING=1\n\n# Windows MSVC 2005 x86\nelse ifeq ($(platform), windows_msvc2005_x86)\n\tCC  = cl.exe\n\tCXX = cl.exe\n\nPATH := $(shell IFS=$$'\\n'; cygpath \"$(VS80COMNTOOLS)../../VC/bin\"):$(PATH)\nPATH := $(PATH):$(shell IFS=$$'\\n'; cygpath \"$(VS80COMNTOOLS)../IDE\")\nINCLUDE := $(shell IFS=$$'\\n'; cygpath \"$(VS80COMNTOOLS)../../VC/include\")\nLIB := $(shell IFS=$$'\\n'; cygpath -w \"$(VS80COMNTOOLS)../../VC/lib\")\nBIN := $(shell IFS=$$'\\n'; cygpath \"$(VS80COMNTOOLS)../../VC/bin\")\n\nWindowsSdkDir := $(INETSDK)\n\nexport INCLUDE := $(INCLUDE);$(INETSDK)/Include;src/drivers/libretro/msvc/msvc-2005\nexport LIB := $(LIB);$(WindowsSdkDir);$(INETSDK)/Lib\nTARGET := $(TARGET_NAME)_libretro.dll\nPSS_STYLE :=2\nLDFLAGS += -DLL\nCFLAGS += -D_CRT_SECURE_NO_DEPRECATE\n\n# Windows MSVC 2003 x86\nelse ifeq ($(platform), windows_msvc2003_x86)\n\tCC  = cl.exe\n\tCXX = cl.exe\n\nPATH := $(shell IFS=$$'\\n'; cygpath \"$(VS71COMNTOOLS)../../Vc7/bin\"):$(PATH)\nPATH := $(PATH):$(shell IFS=$$'\\n'; cygpath \"$(VS71COMNTOOLS)../IDE\")\nINCLUDE := $(shell IFS=$$'\\n'; cygpath \"$(VS71COMNTOOLS)../../Vc7/include\")\nLIB := $(shell IFS=$$'\\n'; cygpath -w \"$(VS71COMNTOOLS)../../Vc7/lib\")\nBIN := $(shell IFS=$$'\\n'; cygpath \"$(VS71COMNTOOLS)../../Vc7/bin\")\n\nWindowsSdkDir := $(INETSDK)\n\nexport INCLUDE := $(INCLUDE);$(INETSDK)/Include;src/drivers/libretro/msvc/msvc-2005\nexport LIB := $(LIB);$(WindowsSdkDir);$(INETSDK)/Lib\nTARGET := $(TARGET_NAME)_libretro.dll\nPSS_STYLE :=2\nLDFLAGS += -DLL\nCFLAGS += -D_CRT_SECURE_NO_DEPRECATE\n\n# Windows\nelse\n\tTARGET := $(TARGET_NAME)_libretro.dll\n\tCC ?= gcc\n\tCXX ?= g++\n\tSHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=$(CORE_DIR)/link.T\n   PSS_STYLE :=2\nendif\n\nifeq ($(DEBUG), 1)\n\tifneq (,$(findstring msvc,$(platform)))\n\t\tifeq ($(STATIC_LINKING),1)\n\t\t\tCFLAGS += -MTd\n\t\t\tCXXFLAGS += -MTd\n\t\telse\n\t\t\tCFLAGS += -MDd\n\t\t\tCXXFLAGS += -MDd\n\t\tendif\n\n\t\tCFLAGS += -Od -Zi -DDEBUG -D_DEBUG\n\t\tCXXFLAGS += -Od -Zi -DDEBUG -D_DEBUG\n\telse\n\t\tCFLAGS += -O0 -g -DDEBUG\n\t\tCXXFLAGS += -O0 -g -DDEBUG\n\tendif\nelse\n\tifneq (,$(findstring msvc,$(platform)))\n\t\tifeq ($(STATIC_LINKING),1)\n\t\t\tCFLAGS += -MT\n\t\t\tCXXFLAGS += -MT\n\t\telse\n\t\t\tCFLAGS += -MD\n\t\t\tCXXFLAGS += -MD\n\t\tendif\n\n\t\tCFLAGS += -O2 -DNDEBUG\n\t\tCXXFLAGS += -O2 -DNDEBUG\n\telse\n\t\tCFLAGS += -O2 -DNDEBUG\n\t\tCXXFLAGS += -O2 -DNDEBUG\n\tendif\nendif\n\nifeq ($(EXTERNAL_ZLIB), 1)\n\tCFLAGS += -DHAVE_EXTERNAL_ZLIB\n\tCXXFLAGS += -DHAVE_EXTERNAL_ZLIB\nendif\n\ninclude Makefile.common\n\nOBJECTS := $(SOURCES_C:.c=.o) $(SOURCES_CXX:.cpp=.o)\n\nDEFINES := $(COREDEFINES) $(PLATFORM_DEFINES)\n\nifeq ($(STATIC_LINKING),1)\n\tDEFINES += -DSTATIC_LINKING\nendif\n\nifeq ($(platform), sncps3)\nWARNING_DEFINES =\nelse ifneq (,$(findstring msvc,$(platform)))\nWARNING_DEFINES =\nLIBM :=\nelse\nWARNING_DEFINES = -Wno-write-strings\nendif\n\nCFLAGS   += $(COREDEFINES) $(fpic) $(WARNING_DEFINES) $(DEFINES) $(ENDIANNESS_DEFINES)\nCXXFLAGS += $(COREDEFINES) $(fpic) $(WARNING_DEFINES) $(DEFINES) $(ENDIANNESS_DEFINES)\nLDFLAGS  += $(LIBM)\n\nifeq ($(platform), psp1)\n\tINCFLAGS += -I$(shell psp-config --pspsdk-path)/include\nendif\n\n\nifneq (,$(findstring msvc,$(platform)))\n\tOBJOUT = -Fo\n\tLINKOUT = -out:\nifeq ($(STATIC_LINKING),1)\n\tLD ?= lib.exe\n\tSTATIC_LINKING=0\nelse\n\tLD = link.exe\nendif\nelse\n\tOBJOUT   = -o\n\tLINKOUT  = -o \n\tLD = $(CC)\nendif\n\nifeq ($(platform), theos_ios)\nCOMMON_FLAGS := -DIOS $(COMMON_DEFINES) $(INCFLAGS) -I$(THEOS_INCLUDE_PATH) -Wno-error\n$(LIBRARY_NAME)_CFLAGS += $(COMMON_FLAGS) $(CFLAGS)\n$(LIBRARY_NAME)_CXXFLAGS += $(COMMON_FLAGS) $(CXXFLAGS)\n${LIBRARY_NAME}_FILES = $(SOURCES_C)\ninclude $(THEOS_MAKE_PATH)/library.mk\nelse\nall: $(TARGET)\n$(TARGET): $(OBJECTS)\nifeq ($(STATIC_LINKING),1)\nifneq (,$(findstring msvc,$(platform)))\n\t$(LD) $(LINKOUT)$@ $(OBJECTS)\nelse\n\t$(AR) rcs $@ $(OBJECTS)\nendif\nelse\n\t$(LD) $(LINKOUT)$@ $(SHARED) $(OBJECTS) $(LDFLAGS) $(LIBS)\nendif\n\n%.o: %.cpp\n\t$(CXX) -c $(OBJOUT)$@ $< $(CXXFLAGS) $(INCFLAGS)\n\n%.o: %.c\n\t$(CC) -c $(OBJOUT)$@ $< $(CFLAGS) $(INCFLAGS)\n\nclean-objs:\n\trm -f $(OBJECTS)\n\nclean:\n\trm -f $(OBJECTS)\n\trm -f $(TARGET)\n\n.PHONY: clean clean-objs\nendif\n\nprint-%:\n\t@echo '$*=$($*)'\n"
  },
  {
    "path": "platform/LibRetro/Makefile.common",
    "content": "\nINCFLAGS       :=   -I ../../core \\\n                    -I ../../core/machine \\\n                    -I ../../core/accessories \\\n                    -I ../../core/datamanager \\\n                    -I ../../core/interpreter \\\n                    -I ../../core/libraries \\\n                    -I ../../core/overlay\n\nCOREDEFINES    = -D__LIBRETRO__\n\nifneq (,$(findstring msvc,$(platform)))\nCOREDEFINES += -DINLINE=_inline\nelse\nCOREDEFINES += -DINLINE=inline\nendif\n\nifeq ($(PSS_STYLE),2)\nCOREDEFINES += -DPSS_STYLE=2\nelse\nCOREDEFINES += -DPSS_STYLE=1\nendif\n\n# Add C sourcecode files to this\nSOURCES_C     :=  $(wildcard ../../core/*.c)\\\n                  $(wildcard ../../core/*/*.c)\\\n                  $(wildcard ../../libretro/*.c)\n\n# Add C++ sourcecode files to this\nSOURCES_CXX   := "
  },
  {
    "path": "platform/LibRetro/jni/Android.mk",
    "content": "LOCAL_PATH := $(call my-dir)\n\nSRCDIR := $(LOCAL_PATH)/../../../\n\nINCFLAGS       :=   -I $(SRCDIR)/core \\\n                    -I $(SRCDIR)/core/machine \\\n                    -I $(SRCDIR)/core/accessories \\\n                    -I $(SRCDIR)/core/datamanager \\\n                    -I $(SRCDIR)/core/interpreter \\\n                    -I $(SRCDIR)/core/libraries \\\n                    -I $(SRCDIR)/core/overlay\n\nCOREFLAGS := -fomit-frame-pointer -ffast-math -D__LIBRETRO__ $(INCFLAGS)\nCOREFLAGS += -DGB_INTERNAL -DDISABLE_DEBUGGER -DPLATFORM_ANDROID\n\nGIT_VERSION := \" $(shell git rev-parse --short HEAD || echo unknown)\"\nifneq ($(GIT_VERSION),\" unknown\")\n  COREFLAGS += -DGIT_VERSION=\\\"$(GIT_VERSION)\\\"\nendif\n\nSRCFILES     :=  $(wildcard $(SRCDIR)/core/*.c)\\\n                  $(wildcard $(SRCDIR)/core/*/*.c)\\\n                  $(wildcard $(SRCDIR)/libretro/*.c)\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE       := retro\nLOCAL_SRC_FILES    := $(SRCFILES)\nLOCAL_CPPFLAGS     := -std=c++17 $(COREFLAGS)\nLOCAL_CFLAGS       := $(COREFLAGS)\nLOCAL_LDFLAGS      := -Wl,-version-script=../link.T\nLOCAL_CPP_FEATURES := exceptions rtti\ninclude $(BUILD_SHARED_LIBRARY)"
  },
  {
    "path": "platform/LibRetro/jni/Application.mk",
    "content": "APP_ABI := all\nAPP_STL := c++_static"
  },
  {
    "path": "platform/LibRetro/link.T",
    "content": "{\n   global: retro_*;\n   local: *;\n};\n"
  },
  {
    "path": "platform/Linux/README.md",
    "content": "Build for LINUX\n===============\n\n## Installing Dependencies\n\n### Ubuntu\n> Tested with Ubuntu 18.04\n\n```bash\nsudo apt install git make build-essential libsdl2-dev\n```\n\n### Debian\n> Tested with Debian 11 (Bullseye)\n\n```bash\nsudo apt install git make build-essential libsdl2-dev libdrm-dev libgbm-dev\n```\n\n### Arch Linux\n> Tested with Arch Linux (x86_64, kernel version 6.9.3-arch1-1)\n\n```bash\nsudo pacman -S git make base-devel sdl2\n```\n\n## Building and Running\nThese steps have been tested on all the distributions listed above and work without modification.\n```bash\ngit clone https://github.com/timoinutilis/lowres-nx.git\ncd lowres-nx/platform/Linux/\nmake\n./output/LowResNX\n```\n"
  },
  {
    "path": "platform/Linux/makefile",
    "content": "# Declaration of variables\nCC = gcc\nCC_FLAGS =  -D __LINUX__ -w -I ../../core -I ../../core/machine -I ../../core/accessories -I ../../core/datamanager -I ../../core/interpreter -I ../../core/libraries -I ../../core/overlay -lSDL2main -lSDL2 -lm\nCC_FLAGS_MAIN =  -D __LINUX__ -lSDL2main -lSDL2 -lm `sdl2-config --cflags --static-libs`\n\n# File names\nEXEC = output/LowResNX\nSOURCES = $(wildcard ../../core/*.c) $(wildcard ../../core/*/*.c) $(wildcard ../../sdl/*.c)\nOBJECTS = $(SOURCES:.c=.bc)\n \n# Main target\n$(EXEC): $(OBJECTS)\n\t@mkdir -p $(@D)\n\t$(CC) $(OBJECTS) -o $(EXEC) $(CC_FLAGS_MAIN) \n\n# To obtain object files\n%.bc: %.c\n\t$(CC) -c $(CC_FLAGS) $< -o $@\n\n# To remove generated files\nclean:\n\trm -f $(OBJECTS)\n"
  },
  {
    "path": "platform/Windows/LowRes NX Win/LowRes NX Win.vcxproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup Label=\"ProjectConfigurations\">\n    <ProjectConfiguration Include=\"Debug|Win32\">\n      <Configuration>Debug</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|Win32\">\n      <Configuration>Release</Configuration>\n      <Platform>Win32</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Debug|x64\">\n      <Configuration>Debug</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n    <ProjectConfiguration Include=\"Release|x64\">\n      <Configuration>Release</Configuration>\n      <Platform>x64</Platform>\n    </ProjectConfiguration>\n  </ItemGroup>\n  <PropertyGroup Label=\"Globals\">\n    <VCProjectVersion>15.0</VCProjectVersion>\n    <ProjectGuid>{5F65EDCE-D405-4962-BD3B-65C49E160B3E}</ProjectGuid>\n    <RootNamespace>LowResNXWin</RootNamespace>\n    <WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>true</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\n    <ConfigurationType>Application</ConfigurationType>\n    <UseDebugLibraries>false</UseDebugLibraries>\n    <PlatformToolset>v141</PlatformToolset>\n    <WholeProgramOptimization>true</WholeProgramOptimization>\n    <CharacterSet>MultiByte</CharacterSet>\n  </PropertyGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\n  <ImportGroup Label=\"ExtensionSettings\">\n  </ImportGroup>\n  <ImportGroup Label=\"Shared\">\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <ImportGroup Label=\"PropertySheets\" Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\n  </ImportGroup>\n  <PropertyGroup Label=\"UserMacros\" />\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <TargetName>LowRes NX</TargetName>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <TargetName>LowRes NX</TargetName>\n  </PropertyGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <SDLCheck>false</SDLCheck>\n      <ConformanceMode>true</ConformanceMode>\n      <AdditionalIncludeDirectories>..\\..\\..\\sdl;..\\..\\..\\core\\overlay;..\\..\\..\\core\\machine;..\\..\\..\\core\\libraries;..\\..\\..\\core\\interpreter;..\\..\\..\\core\\datamanager;..\\..\\..\\core\\accessories;..\\..\\..\\core;C:\\Users\\BARBARA\\Documents\\Timo\\SDL2\\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n    </ClCompile>\n    <Link>\n      <AdditionalLibraryDirectories>C:\\Users\\BARBARA\\Documents\\Timo\\SDL2\\lib\\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <SubSystem>Windows</SubSystem>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>Disabled</Optimization>\n      <SDLCheck>false</SDLCheck>\n      <ConformanceMode>true</ConformanceMode>\n      <AdditionalIncludeDirectories>..\\..\\..\\sdl;..\\..\\..\\core\\overlay;..\\..\\..\\core\\machine;..\\..\\..\\core\\libraries;..\\..\\..\\core\\interpreter;..\\..\\..\\core\\datamanager;..\\..\\..\\core\\accessories;..\\..\\..\\core;C:\\Users\\BARBARA\\Documents\\Timo\\SDL2\\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>false</SDLCheck>\n      <ConformanceMode>true</ConformanceMode>\n      <AdditionalIncludeDirectories>..\\..\\..\\sdl;..\\..\\..\\core\\overlay;..\\..\\..\\core\\machine;..\\..\\..\\core\\libraries;..\\..\\..\\core\\interpreter;..\\..\\..\\core\\datamanager;..\\..\\..\\core\\accessories;..\\..\\..\\core;C:\\Users\\BARBARA\\Documents\\Timo\\SDL2\\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>\n      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n    </ClCompile>\n    <Link>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n      <AdditionalLibraryDirectories>C:\\Users\\BARBARA\\Documents\\Timo\\SDL2\\lib\\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\n      <AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>\n      <SubSystem>Windows</SubSystem>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <ClCompile>\n      <WarningLevel>Level3</WarningLevel>\n      <Optimization>MaxSpeed</Optimization>\n      <FunctionLevelLinking>true</FunctionLevelLinking>\n      <IntrinsicFunctions>true</IntrinsicFunctions>\n      <SDLCheck>false</SDLCheck>\n      <ConformanceMode>true</ConformanceMode>\n      <AdditionalIncludeDirectories>..\\..\\..\\sdl;..\\..\\..\\core\\overlay;..\\..\\..\\core\\machine;..\\..\\..\\core\\libraries;..\\..\\..\\core\\interpreter;..\\..\\..\\core\\datamanager;..\\..\\..\\core\\accessories;..\\..\\..\\core;C:\\Users\\BARBARA\\Documents\\Timo\\SDL2\\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n    </ClCompile>\n    <Link>\n      <EnableCOMDATFolding>true</EnableCOMDATFolding>\n      <OptimizeReferences>true</OptimizeReferences>\n    </Link>\n  </ItemDefinitionGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\core\\accessories\\disk_drive.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\boot_intro.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\core.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\core_delegate.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\datamanager\\data_manager.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\charsets.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_audio.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_background.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_control.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_data.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_files.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_io.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_maths.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_memory.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_screen.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_sprites.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_strings.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_subs.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_text.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_variables.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\data.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\error.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\interpreter.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\interpreter_utils.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\labels.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\rcstring.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\string_utils.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\token.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\tokenizer.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\value.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\variables.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\audio_lib.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\default_characters.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\sprites_lib.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\startup_sequence.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\text_lib.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\machine\\audio_chip.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\machine\\machine.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\machine\\video_chip.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\overlay\\overlay.c\" />\n    <ClCompile Include=\"..\\..\\..\\core\\overlay\\overlay_data.c\" />\n    <ClCompile Include=\"..\\..\\..\\sdl\\dev_menu.c\" />\n    <ClCompile Include=\"..\\..\\..\\sdl\\main.c\" />\n    <ClCompile Include=\"..\\..\\..\\sdl\\runner.c\" />\n    <ClCompile Include=\"..\\..\\..\\sdl\\screenshot.c\" />\n    <ClCompile Include=\"..\\..\\..\\sdl\\settings.c\" />\n    <ClCompile Include=\"..\\..\\..\\sdl\\system_paths.c\" />\n    <ClCompile Include=\"..\\..\\..\\sdl\\utils.c\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"LowRes NX Win.rc\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Image Include=\"icon1.ico\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\core\\accessories\\disk_drive.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\boot_intro.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\core.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\core_delegate.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\core_stats.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\datamanager\\data_manager.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\charsets.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_audio.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_background.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_control.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_data.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_files.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_io.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_maths.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_memory.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_screen.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_sprites.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_strings.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_subs.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_text.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_variables.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\data.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\error.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\interpreter.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\interpreter_config.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\interpreter_utils.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\labels.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\rcstring.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\string_utils.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\token.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\tokenizer.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\value.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\variables.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\audio_lib.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\default_characters.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\sprites_lib.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\startup_sequence.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\text_lib.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\machine\\audio_chip.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\machine\\io_chip.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\machine\\machine.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\machine\\video_chip.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\overlay\\overlay.h\" />\n    <ClInclude Include=\"..\\..\\..\\core\\overlay\\overlay_data.h\" />\n  </ItemGroup>\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\n  <ImportGroup Label=\"ExtensionTargets\">\n  </ImportGroup>\n</Project>"
  },
  {
    "path": "platform/Windows/LowRes NX Win/LowRes NX Win.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <ItemGroup>\n    <Filter Include=\"Source Files\">\n      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>\n      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>\n    </Filter>\n    <Filter Include=\"Header Files\">\n      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>\n      <Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>\n    </Filter>\n    <Filter Include=\"Resource Files\">\n      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>\n      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>\n    </Filter>\n  </ItemGroup>\n  <ItemGroup>\n    <ClCompile Include=\"..\\..\\..\\core\\accessories\\disk_drive.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\core.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\core_delegate.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\datamanager\\data_manager.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\charsets.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_background.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_control.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_data.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_files.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_io.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_maths.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_memory.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_screen.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_sprites.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_strings.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_subs.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_text.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_variables.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\data.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\error.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\interpreter.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\interpreter_utils.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\labels.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\rcstring.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\string_utils.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\token.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\tokenizer.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\value.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\variables.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\default_characters.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\sprites_lib.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\startup_sequence.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\text_lib.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\machine\\audio_chip.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\machine\\machine.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\machine\\video_chip.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\overlay\\overlay.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\overlay\\overlay_data.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\sdl\\main.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\sdl\\settings.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\interpreter\\cmd_audio.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\libraries\\audio_lib.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\sdl\\dev_menu.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\sdl\\runner.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\sdl\\screenshot.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\sdl\\system_paths.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\sdl\\utils.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n    <ClCompile Include=\"..\\..\\..\\core\\boot_intro.c\">\n      <Filter>Source Files</Filter>\n    </ClCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <ResourceCompile Include=\"LowRes NX Win.rc\">\n      <Filter>Resource Files</Filter>\n    </ResourceCompile>\n  </ItemGroup>\n  <ItemGroup>\n    <Image Include=\"icon1.ico\">\n      <Filter>Resource Files</Filter>\n    </Image>\n  </ItemGroup>\n  <ItemGroup>\n    <ClInclude Include=\"..\\..\\..\\core\\boot_intro.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\core.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\core_delegate.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\core_stats.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\accessories\\disk_drive.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\datamanager\\data_manager.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\charsets.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_audio.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_background.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_control.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_data.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_files.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_io.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_maths.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_memory.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_screen.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_sprites.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_strings.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_subs.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_text.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\cmd_variables.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\data.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\error.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\interpreter.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\interpreter_config.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\interpreter_utils.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\labels.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\rcstring.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\string_utils.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\token.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\tokenizer.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\value.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\interpreter\\variables.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\audio_lib.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\default_characters.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\sprites_lib.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\startup_sequence.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\libraries\\text_lib.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\machine\\audio_chip.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\machine\\io_chip.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\machine\\machine.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\machine\\video_chip.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\overlay\\overlay.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n    <ClInclude Include=\"..\\..\\..\\core\\overlay\\overlay_data.h\">\n      <Filter>Header Files</Filter>\n    </ClInclude>\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "platform/Windows/LowRes NX Win/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by LowRes NX Win.rc\n//\n#define IDI_ICON1                       101\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        102\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "platform/Windows/LowRes NX Win/resource1.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by LowRes NX Win.rc\n//\n#define IDI_ICON1                       101\n\n// Next default values for new objects\n// \n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        103\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "platform/Windows/LowRes NX Win.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.27703.2035\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"LowRes NX Win\", \"LowRes NX Win\\LowRes NX Win.vcxproj\", \"{5F65EDCE-D405-4962-BD3B-65C49E160B3E}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Debug|x64.Build.0 = Debug|x64\n\t\t{5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Debug|x86.ActiveCfg = Debug|Win32\n\t\t{5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Debug|x86.Build.0 = Debug|Win32\n\t\t{5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Release|x64.ActiveCfg = Release|x64\n\t\t{5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Release|x64.Build.0 = Release|x64\n\t\t{5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Release|x86.ActiveCfg = Release|Win32\n\t\t{5F65EDCE-D405-4962-BD3B-65C49E160B3E}.Release|x86.Build.0 = Release|Win32\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {13C7A20F-9918-41F2-B5D1-CCE0C942A3EC}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/AppDelegate.swift",
    "content": "//\n//  AppDelegate.swift\n//  LowRes NX iOS\n//\n//  Created by Timo Kloss on 1/9/17.\n//  Copyright © 2017 Inutilis Software. All rights reserved.\n//\n\nimport UIKit\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    var window: UIWindow?\n    \n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {\n                \n        return true\n    }\n\n    func applicationWillResignActive(_ application: UIApplication) {\n        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n    }\n\n    func applicationDidEnterBackground(_ application: UIApplication) {\n        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n    }\n\n    func applicationWillEnterForeground(_ application: UIApplication) {\n        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.\n    }\n\n    func applicationDidBecomeActive(_ application: UIApplication) {\n        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n    }\n\n    func applicationWillTerminate(_ application: UIApplication) {\n        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n    }\n\n\n}\n\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"29x29\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"29x29\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"40x40\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"40x40\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"60x60\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"60x60\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"29x29\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"29x29\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"40x40\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"40x40\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"76x76\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"76x76\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"size\" : \"83.5x83.5\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16F73\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Llm-lL-Icb\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xb3-aO-Qok\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13529\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <device id=\"retina4_7\" orientation=\"portrait\">\n        <adaptation id=\"fullscreen\"/>\n    </device>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13527\"/>\n        <capability name=\"Aspect ratio constraints\" minToolsVersion=\"5.1\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"ViewController\" customModule=\"LowRes_NX\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Spk-aV-ecP\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"20\" width=\"375\" height=\"647\"/>\n                                <subviews>\n                                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"b9G-Li-wZu\" customClass=\"LowResNXView\" customModule=\"LowRes_NX\" customModuleProvider=\"target\">\n                                        <rect key=\"frame\" x=\"27.5\" y=\"195.5\" width=\"320\" height=\"256\"/>\n                                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" constant=\"320\" id=\"7vO-fu-6vJ\"/>\n                                            <constraint firstAttribute=\"width\" secondItem=\"b9G-Li-wZu\" secondAttribute=\"height\" multiplier=\"5:4\" id=\"XSJ-yb-6zl\"/>\n                                        </constraints>\n                                    </view>\n                                </subviews>\n                                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n                                <constraints>\n                                    <constraint firstItem=\"b9G-Li-wZu\" firstAttribute=\"centerY\" secondItem=\"Spk-aV-ecP\" secondAttribute=\"centerY\" id=\"G7P-cN-Giu\"/>\n                                    <constraint firstItem=\"b9G-Li-wZu\" firstAttribute=\"centerX\" secondItem=\"Spk-aV-ecP\" secondAttribute=\"centerX\" id=\"PCW-ev-YSA\"/>\n                                    <constraint firstItem=\"b9G-Li-wZu\" firstAttribute=\"centerX\" secondItem=\"Spk-aV-ecP\" secondAttribute=\"centerX\" id=\"mpj-Ve-WsP\"/>\n                                    <constraint firstItem=\"b9G-Li-wZu\" firstAttribute=\"centerY\" secondItem=\"Spk-aV-ecP\" secondAttribute=\"centerY\" id=\"ooJ-QN-5pg\"/>\n                                </constraints>\n                            </view>\n                        </subviews>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"1\" colorSpace=\"calibratedWhite\"/>\n                        <constraints>\n                            <constraint firstItem=\"Spk-aV-ecP\" firstAttribute=\"top\" secondItem=\"fgk-bn-lmH\" secondAttribute=\"top\" id=\"Nif-xE-qG5\"/>\n                            <constraint firstItem=\"fgk-bn-lmH\" firstAttribute=\"trailing\" secondItem=\"Spk-aV-ecP\" secondAttribute=\"trailing\" id=\"RXa-me-LU6\"/>\n                            <constraint firstItem=\"Spk-aV-ecP\" firstAttribute=\"leading\" secondItem=\"fgk-bn-lmH\" secondAttribute=\"leading\" id=\"kgf-ex-iz0\"/>\n                            <constraint firstItem=\"fgk-bn-lmH\" firstAttribute=\"bottom\" secondItem=\"Spk-aV-ecP\" secondAttribute=\"bottom\" id=\"sZ9-Eo-Z5A\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"fgk-bn-lmH\"/>\n                    </view>\n                    <nil key=\"simulatedStatusBarMetrics\"/>\n                    <connections>\n                        <outlet property=\"containerView\" destination=\"Spk-aV-ecP\" id=\"AEa-Yg-GyL\"/>\n                        <outlet property=\"keyboardConstraint\" destination=\"sZ9-Eo-Z5A\" id=\"uTa-v1-c0M\"/>\n                        <outlet property=\"nxView\" destination=\"b9G-Li-wZu\" id=\"o6z-oE-YdM\"/>\n                        <outlet property=\"widthConstraint\" destination=\"7vO-fu-6vJ\" id=\"LAa-ep-evn\"/>\n                    </connections>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"141.59999999999999\" y=\"138.98050974512745\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/CoreWrapper.swift",
    "content": "//\n//  CoreWrapper.swift\n//  LowRes NX iOS\n//\n//  Created by Timo Kloss on 2/9/17.\n//  Copyright © 2017 Inutilis Software. All rights reserved.\n//\n\nimport Foundation\n\nprotocol CoreWrapperDelegate: class {\n    func coreInterpreterDidFail(coreError: CoreError) -> Void\n    func coreDiskDriveWillAccess(diskDataManager: UnsafeMutablePointer<DataManager>?) -> Bool\n    func coreDiskDriveDidSave(diskDataManager: UnsafeMutablePointer<DataManager>?) -> Void\n    func coreControlsDidChange(controlsInfo: ControlsInfo) -> Void\n}\n\nclass CoreWrapper: NSObject {\n    \n    weak var delegate: CoreWrapperDelegate?\n    \n    var core = Core()\n    private var coreDelegate = CoreDelegate()\n    \n    override init() {\n        super.init()\n        core_init(&core)\n        \n        coreDelegate.context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())\n        coreDelegate.interpreterDidFail = interpreterDidFail\n        coreDelegate.diskDriveWillAccess = diskDriveWillAccess\n        coreDelegate.diskDriveDidSave = diskDriveDidSave\n        coreDelegate.controlsDidChange = controlsDidChange\n        core_setDelegate(&core, &coreDelegate)\n    }\n    \n    deinit {\n        core_deinit(&core)\n    }\n    \n}\n\nclass LowResNXError: NSError {\n    \n    let coreError: CoreError\n    \n    init(error: CoreError, sourceCode: String) {\n        coreError = error\n        let index = sourceCode.index(sourceCode.startIndex, offsetBy: String.IndexDistance(error.sourcePosition))\n        let lineRange = sourceCode.lineRange(for: index ..< index)\n        let lineString = sourceCode[lineRange].trimmingCharacters(in: CharacterSet.whitespaces)\n        let lineNumber = sourceCode.countLines(index: index)\n        \n        let errorString = String(cString:err_getString(error.code))\n        let errorText = \"Error in line \\(lineNumber): \\(errorString)\\n\\(lineString)\"\n        super.init(domain: \"LowResNX\", code: Int(error.code.rawValue), userInfo: [NSLocalizedDescriptionKey: errorText])\n    }\n    \n    required init?(coder aDecoder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n    \n}\n\nextension String {\n\n    func countLines(index: String.Index) -> Int {\n        var count = 1\n        var searchRange = startIndex ..< endIndex\n        while let foundRange = rangeOfCharacter(from: CharacterSet.newlines, options: .literal, range: searchRange), index >= foundRange.upperBound {\n            searchRange = characters.index(after: foundRange.lowerBound) ..< endIndex\n            count += 1\n        }\n        return count;\n    }\n    \n}\n\n//MARK: - Core Delegate Functions Wrapper\n\nfunc interpreterDidFail(context: UnsafeMutableRawPointer?, coreError: CoreError) -> Void {\n    let wrapper = Unmanaged<CoreWrapper>.fromOpaque(context!).takeUnretainedValue()\n    wrapper.delegate?.coreInterpreterDidFail(coreError: coreError)\n}\n\nfunc diskDriveWillAccess(context: UnsafeMutableRawPointer?, diskDataManager: UnsafeMutablePointer<DataManager>?) -> Bool {\n    let wrapper = Unmanaged<CoreWrapper>.fromOpaque(context!).takeUnretainedValue()\n    return wrapper.delegate?.coreDiskDriveWillAccess(diskDataManager: diskDataManager) ?? true\n}\n\nfunc diskDriveDidSave(context: UnsafeMutableRawPointer?, diskDataManager: UnsafeMutablePointer<DataManager>?) -> Void {\n    let wrapper = Unmanaged<CoreWrapper>.fromOpaque(context!).takeUnretainedValue()\n    wrapper.delegate?.coreDiskDriveDidSave(diskDataManager: diskDataManager)\n}\n\nfunc controlsDidChange(context: UnsafeMutableRawPointer?, controlsInfo: ControlsInfo) -> Void {\n    let wrapper = Unmanaged<CoreWrapper>.fromOpaque(context!).takeUnretainedValue()\n    wrapper.delegate?.coreControlsDidChange(controlsInfo: controlsInfo)\n}\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>0.1</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIStatusBarHidden</key>\n\t<true/>\n\t<key>UIStatusBarStyle</key>\n\t<string>UIStatusBarStyleDefault</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/LowResNX-Bridging-Header.h",
    "content": "//\n//  LowResNX-Bridging-Header.h\n//  LowRes NX iOS\n//\n//  Created by Timo Kloss on 23/4/17.\n//  Copyright © 2017 Inutilis Software. All rights reserved.\n//\n\n#include \"core.h\"\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/LowResNXView.swift",
    "content": "//\n//  LowResNXView.swift\n//  LowRes NX iOS\n//\n//  Created by Timo Kloss on 1/9/17.\n//  Copyright © 2017 Inutilis Software. All rights reserved.\n//\n\nimport UIKit\n\nclass LowResNXView: UIView {\n    var coreWrapper: CoreWrapper?\n\n    private var data: UnsafeMutablePointer<UInt8>?\n    private var dataProvider: CGDataProvider?\n    \n    required init?(coder aDecoder: NSCoder) {\n        super.init(coder: aDecoder)\n        \n        let dataLength = Int(SCREEN_WIDTH) * Int(SCREEN_HEIGHT) * 4;\n        data = UnsafeMutablePointer<UInt8>.allocate(capacity: dataLength)\n        var callbacks = CGDataProviderDirectCallbacks(version: 0, getBytePointer: getBytePointerCallback, releaseBytePointer: nil, getBytesAtPosition: nil, releaseInfo: nil)\n        dataProvider = CGDataProvider(directInfo: data, size: off_t(dataLength), callbacks: &callbacks)\n    }\n    \n    override func awakeFromNib() {\n        super.awakeFromNib()\n        isMultipleTouchEnabled = true\n    }\n    \n    func render() {\n        if let coreWrapper = coreWrapper, let dataProvider = dataProvider {\n            video_renderScreen(&coreWrapper.core, data, SCREEN_WIDTH*4)\n            let image = CGImage(width: Int(SCREEN_WIDTH), height: Int(SCREEN_HEIGHT), bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: Int(SCREEN_WIDTH)*4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipLast.rawValue), provider: dataProvider, decode: nil, shouldInterpolate: false, intent: .defaultIntent)\n            \n            layer.contents = image\n            layer.magnificationFilter = kCAFilterNearest\n        }\n    }\n    \n    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {\n        if let coreWrapper = coreWrapper {\n            for touch in touches {\n                let point = screenPoint(touch: touch)\n                core_touchPressed(&coreWrapper.core, Int32(point.x), Int32(point.y), Unmanaged.passUnretained(touch).toOpaque())\n            }\n        }\n    }\n    \n    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {\n        if let coreWrapper = coreWrapper {\n            for touch in touches {\n                let point = screenPoint(touch: touch)\n                core_touchDragged(&coreWrapper.core, Int32(point.x), Int32(point.y), Unmanaged.passUnretained(touch).toOpaque())\n            }\n        }\n    }\n    \n    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {\n        if let coreWrapper = coreWrapper {\n            for touch in touches {\n                core_touchReleased(&coreWrapper.core, Unmanaged.passUnretained(touch).toOpaque())\n            }\n        }\n    }\n    \n    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {\n        if let coreWrapper = coreWrapper {\n            for touch in touches {\n                core_touchReleased(&coreWrapper.core, Unmanaged.passUnretained(touch).toOpaque())\n            }\n        }\n    }\n    \n    private func screenPoint(touch: UITouch) -> CGPoint {\n        let viewPoint = touch.location(in: self)\n        let x = viewPoint.x * CGFloat(SCREEN_WIDTH) / bounds.size.width;\n        let y = viewPoint.y * CGFloat(SCREEN_HEIGHT) / bounds.size.height;\n        return CGPoint(x: x, y: y);\n    }\n\n}\n\nfunc getBytePointerCallback(_ data: UnsafeMutableRawPointer?) -> UnsafeRawPointer? {\n    return UnsafeRawPointer(data)\n}\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS/ViewController.swift",
    "content": "//\n//  ViewController.swift\n//  LowRes NX iOS\n//\n//  Created by Timo Kloss on 1/9/17.\n//  Copyright © 2017 Inutilis Software. All rights reserved.\n//\n\nimport UIKit\nimport GameController\n\nclass ViewController: UIViewController, UIKeyInput, CoreWrapperDelegate {\n    \n    @IBOutlet weak var nxView: LowResNXView!\n    @IBOutlet weak var containerView: UIView!\n    @IBOutlet weak var widthConstraint: NSLayoutConstraint!\n    @IBOutlet weak var keyboardConstraint: NSLayoutConstraint!\n    \n    var programSourceCode: String?\n    var coreWrapper = CoreWrapper()\n    var displayLink: CADisplayLink?\n    var compilerError: Error?\n    \n    var pixelExactScaling: Bool = true {\n        didSet {\n            view.setNeedsLayout()\n        }\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        if let filePath = Bundle.main.path(forResource: \"program\", ofType: \"nx\") {\n            do {\n                let programSourceCode = try String(contentsOfFile: filePath, encoding: .ascii)\n                let cString = programSourceCode.cString(using: .ascii)\n                let error = itp_compileProgram(&coreWrapper.core, cString)\n                if error.code != ErrorNone {\n                    compilerError = LowResNXError(error: error, sourceCode: programSourceCode)\n                } else {\n                    self.programSourceCode = programSourceCode\n                }\n            } catch {\n                compilerError = error\n            }\n        }\n        \n        nxView.coreWrapper = coreWrapper\n        \n        coreWrapper.delegate = self\n        core_willRunProgram(&coreWrapper.core, 0)\n        configureGameControllers()\n        \n        let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))\n        view.addGestureRecognizer(recognizer)\n        \n        let displayLink = CADisplayLink(target: self, selector: #selector(update))\n        self.displayLink = displayLink\n        \n        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)\n        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)\n        NotificationCenter.default.addObserver(self, selector: #selector(controllerDidConnect), name: .GCControllerDidConnect, object: nil)\n        NotificationCenter.default.addObserver(self, selector: #selector(controllerDidDisconnect), name: .GCControllerDidDisconnect, object: nil)\n    }\n    \n    deinit {\n        NotificationCenter.default.removeObserver(self)\n    }\n    \n    override func viewDidAppear(_ animated: Bool) {\n        super.viewDidAppear(animated)\n        displayLink!.add(to: .current, forMode: .defaultRunLoopMode)\n        if let error = compilerError {\n            let alert = UIAlertController(title: \"Cannot Run Program\", message: error.localizedDescription, preferredStyle: .alert)\n            alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n            present(alert, animated: true, completion: nil)\n        }\n    }\n    \n    override var prefersStatusBarHidden: Bool {\n        return true\n    }\n    \n    override func viewDidLayoutSubviews() {\n        super.viewDidLayoutSubviews()\n        let screenWidth = containerView.bounds.size.width\n        let screenHeight = containerView.bounds.size.height\n        var maxWidthFactor: CGFloat\n        var maxHeightFactor: CGFloat\n        if pixelExactScaling {\n            let scale: CGFloat = view.window?.screen.scale ?? 1.0\n            maxWidthFactor = floor(screenWidth * scale / CGFloat(SCREEN_WIDTH)) / scale\n            maxHeightFactor = floor(screenHeight * scale / CGFloat(SCREEN_HEIGHT)) / scale\n        } else {\n            maxWidthFactor = screenWidth / CGFloat(SCREEN_WIDTH)\n            maxHeightFactor = screenHeight / CGFloat(SCREEN_HEIGHT)\n        }\n        widthConstraint.constant = (maxWidthFactor < maxHeightFactor) ? maxWidthFactor * CGFloat(SCREEN_WIDTH) : maxHeightFactor * CGFloat(SCREEN_WIDTH)\n    }\n    \n    @objc func update(displaylink: CADisplayLink) {\n        updateGameControllers()\n        core_update(&coreWrapper.core)\n        nxView.render()\n    }\n    \n    func configureGameControllers() {\n        let gameControllers = GCController.controllers()\n        \n        core_setNumPhysicalGamepads(&coreWrapper.core, Int32(gameControllers.count))\n        \n        var count = 0\n        for gameController in gameControllers {\n            gameController.playerIndex = GCControllerPlayerIndex(rawValue: count)!\n            gameController.controllerPausedHandler = { [weak self] (controller) in\n                if let coreWrapper = self?.coreWrapper {\n                    core_pausePressed(&coreWrapper.core)\n                }\n            }\n            count += 1\n        }\n    }\n    \n    func updateGameControllers() {\n        for gameController in GCController.controllers() {\n            if let gamepad = gameController.gamepad, gameController.playerIndex != .indexUnset {\n                var up = gamepad.dpad.up.isPressed\n                var down = gamepad.dpad.down.isPressed\n                var left = gamepad.dpad.left.isPressed\n                var right = gamepad.dpad.right.isPressed\n                if let stick = gameController.extendedGamepad?.leftThumbstick {\n                    up = up || stick.up.isPressed\n                    down = down || stick.down.isPressed\n                    left = left || stick.left.isPressed\n                    right = right || stick.right.isPressed\n                }\n                let buttonA = gamepad.buttonA.isPressed || gamepad.buttonX.isPressed\n                let buttonB = gamepad.buttonB.isPressed || gamepad.buttonY.isPressed\n                core_setGamepad(&coreWrapper.core, Int32(gameController.playerIndex.rawValue), up, down, left, right, buttonA, buttonB)\n            }\n        }\n    }\n    \n    @objc func handleTap(sender: UITapGestureRecognizer) {\n        if sender.state == .ended {\n            if core_getKeyboardEnabled(&coreWrapper.core) {\n                becomeFirstResponder()\n            }\n        }\n    }\n    \n    override var canBecomeFirstResponder: Bool {\n        return core_getKeyboardEnabled(&coreWrapper.core)\n    }\n    \n    @objc func keyboardWillShow(_ notification: NSNotification) {\n        if let frameValue = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue {\n            let frame = frameValue.cgRectValue\n            keyboardConstraint.constant = view.bounds.size.height - frame.origin.y\n            UIView.animate(withDuration: 0.3, animations: { \n                self.view.layoutIfNeeded()\n            })\n        }\n    }\n\n    @objc func keyboardWillHide(_ notification: NSNotification) {\n        keyboardConstraint.constant = 0\n        UIView.animate(withDuration: 0.3, animations: {\n            self.view.layoutIfNeeded()\n        })\n    }\n    \n    @objc func controllerDidConnect(_ notification: NSNotification) {\n        configureGameControllers()\n    }\n\n    @objc func controllerDidDisconnect(_ notification: NSNotification) {\n        configureGameControllers()\n    }\n    \n    // MARK: - Core Wrapper Delegate\n    \n    func coreInterpreterDidFail(coreError: CoreError) {\n        let interpreterError = LowResNXError(error: coreError, sourceCode: programSourceCode!)\n        let alert = UIAlertController(title: \"Runtime Error\", message: interpreterError.localizedDescription, preferredStyle: .alert)\n        alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n        present(alert, animated: true, completion: nil)\n    }\n    \n    func coreDiskDriveWillAccess(diskDataManager: UnsafeMutablePointer<DataManager>?) -> Bool {\n        return true\n    }\n    \n    func coreDiskDriveDidSave(diskDataManager: UnsafeMutablePointer<DataManager>?) {\n    }\n    \n    func coreControlsDidChange(controlsInfo: ControlsInfo) {\n        if controlsInfo.isKeyboardEnabled {\n            becomeFirstResponder()\n        } else {\n            resignFirstResponder()\n        }\n    }\n    \n    // MARK: - UIKeyInput\n    \n    var autocorrectionType: UITextAutocorrectionType = .no\n    var spellCheckingType: UITextSpellCheckingType = .no\n    var keyboardAppearance: UIKeyboardAppearance = .dark\n    \n    var hasText: Bool {\n        return true\n    }\n    \n    func insertText(_ text: String) {\n        if text == \"\\n\" {\n            core_returnPressed(&coreWrapper.core)\n        } else if let key = text.uppercased().unicodeScalars.first?.value {\n            if key < 127 {\n                core_keyPressed(&coreWrapper.core, Int8(key))\n            }\n        }\n    }\n    \n    func deleteBackward() {\n        core_backspacePressed(&coreWrapper.core)\n    }\n    \n    // this is from UITextInput, needed because of crash on iPhone 6 keyboard (left/right arrows)\n    var selectedTextRange: UITextRange? {\n        return nil\n    }\n    \n}\n\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t9205E8DA1F599F7300927A3F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8D91F599F7300927A3F /* AppDelegate.swift */; };\n\t\t9205E8DC1F599F7300927A3F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8DB1F599F7300927A3F /* ViewController.swift */; };\n\t\t9205E8DF1F599F7300927A3F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9205E8DD1F599F7300927A3F /* Main.storyboard */; };\n\t\t9205E8E11F599F7300927A3F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9205E8E01F599F7300927A3F /* Assets.xcassets */; };\n\t\t9205E8E41F599F7300927A3F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9205E8E21F599F7300927A3F /* LaunchScreen.storyboard */; };\n\t\t9205E92D1F599FE000927A3F /* disk_drive.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8ED1F599FE000927A3F /* disk_drive.c */; };\n\t\t9205E92E1F599FE000927A3F /* core.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8EF1F599FE000927A3F /* core.c */; };\n\t\t9205E92F1F599FE000927A3F /* cmd_background.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8F31F599FE000927A3F /* cmd_background.c */; };\n\t\t9205E9301F599FE000927A3F /* cmd_control.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8F51F599FE000927A3F /* cmd_control.c */; };\n\t\t9205E9311F599FE000927A3F /* cmd_data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8F71F599FE000927A3F /* cmd_data.c */; };\n\t\t9205E9321F599FE000927A3F /* cmd_files.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8F91F599FE000927A3F /* cmd_files.c */; };\n\t\t9205E9331F599FE000927A3F /* cmd_io.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8FB1F599FE000927A3F /* cmd_io.c */; };\n\t\t9205E9341F599FE000927A3F /* cmd_maths.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8FD1F599FE000927A3F /* cmd_maths.c */; };\n\t\t9205E9351F599FE000927A3F /* cmd_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E8FF1F599FE000927A3F /* cmd_memory.c */; };\n\t\t9205E9361F599FE000927A3F /* cmd_screen.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9011F599FE000927A3F /* cmd_screen.c */; };\n\t\t9205E9371F599FE000927A3F /* cmd_sprites.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9031F599FE000927A3F /* cmd_sprites.c */; };\n\t\t9205E9381F599FE000927A3F /* cmd_strings.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9051F599FE000927A3F /* cmd_strings.c */; };\n\t\t9205E9391F599FE000927A3F /* cmd_text.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9071F599FE000927A3F /* cmd_text.c */; };\n\t\t9205E93A1F599FE000927A3F /* cmd_variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9091F599FE000927A3F /* cmd_variables.c */; };\n\t\t9205E93B1F599FE000927A3F /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E90B1F599FE000927A3F /* data.c */; };\n\t\t9205E93C1F599FE000927A3F /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E90D1F599FE000927A3F /* error.c */; };\n\t\t9205E93D1F599FE000927A3F /* interpreter.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E90F1F599FE000927A3F /* interpreter.c */; };\n\t\t9205E93E1F599FE000927A3F /* interpreter_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9121F599FE000927A3F /* interpreter_utils.c */; };\n\t\t9205E93F1F599FE000927A3F /* labels.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9141F599FE000927A3F /* labels.c */; };\n\t\t9205E9401F599FE000927A3F /* rcstring.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9161F599FE000927A3F /* rcstring.c */; };\n\t\t9205E9411F599FE000927A3F /* token.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9181F599FE000927A3F /* token.c */; };\n\t\t9205E9421F599FE000927A3F /* value.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E91A1F599FE000927A3F /* value.c */; };\n\t\t9205E9431F599FE000927A3F /* variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E91C1F599FE000927A3F /* variables.c */; };\n\t\t9205E9441F599FE000927A3F /* default_characters.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E91F1F599FE000927A3F /* default_characters.c */; };\n\t\t9205E9451F599FE000927A3F /* sprites_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9211F599FE000927A3F /* sprites_lib.c */; };\n\t\t9205E9461F599FE000927A3F /* text_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9231F599FE000927A3F /* text_lib.c */; };\n\t\t9205E9471F599FE000927A3F /* audio_chip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9261F599FE000927A3F /* audio_chip.c */; };\n\t\t9205E9481F599FE000927A3F /* machine.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E9291F599FE000927A3F /* machine.c */; };\n\t\t9205E9491F599FE000927A3F /* video_chip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9205E92B1F599FE000927A3F /* video_chip.c */; };\n\t\t9205E94C1F59A96600927A3F /* program.nx in Resources */ = {isa = PBXBuildFile; fileRef = 9205E94B1F59A96600927A3F /* program.nx */; };\n\t\t9205E94E1F59BEE900927A3F /* LowResNXView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9205E94D1F59BEE900927A3F /* LowResNXView.swift */; };\n\t\t92521BCF1F5B105E0078A7C7 /* CoreWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92521BCE1F5B105E0078A7C7 /* CoreWrapper.swift */; };\n\t\t9286A7141F8FB816005C9EC1 /* charsets.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A70E1F8FB815005C9EC1 /* charsets.c */; };\n\t\t9286A7151F8FB816005C9EC1 /* string_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A7111F8FB816005C9EC1 /* string_utils.c */; };\n\t\t9286A7161F8FB816005C9EC1 /* tokenizer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A7121F8FB816005C9EC1 /* tokenizer.c */; };\n\t\t9286A71A1F8FB84A005C9EC1 /* data_manager.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A7181F8FB84A005C9EC1 /* data_manager.c */; };\n\t\t9286A71C1F8FCE1A005C9EC1 /* core_delegate.c in Sources */ = {isa = PBXBuildFile; fileRef = 9286A71B1F8FCE1A005C9EC1 /* core_delegate.c */; };\n\t\t92AE47D41F6D94A1008BD49C /* overlay.c in Sources */ = {isa = PBXBuildFile; fileRef = 92AE47D01F6D94A1008BD49C /* overlay.c */; };\n\t\t92AE47D51F6D94A1008BD49C /* overlay_data.c in Sources */ = {isa = PBXBuildFile; fileRef = 92AE47D21F6D94A1008BD49C /* overlay_data.c */; };\n\t\t92ED99731FA4F7B90073559B /* startup_sequence.c in Sources */ = {isa = PBXBuildFile; fileRef = 92ED99711FA4F7B80073559B /* startup_sequence.c */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t9205E8D61F599F7300927A3F /* LowRes NX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"LowRes NX.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t9205E8D91F599F7300927A3F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t9205E8DB1F599F7300927A3F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = \"<group>\"; };\n\t\t9205E8E01F599F7300927A3F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t9205E8E51F599F7300927A3F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t9205E8ED1F599FE000927A3F /* disk_drive.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = disk_drive.c; sourceTree = \"<group>\"; };\n\t\t9205E8EE1F599FE000927A3F /* disk_drive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = disk_drive.h; sourceTree = \"<group>\"; };\n\t\t9205E8EF1F599FE000927A3F /* core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core.c; sourceTree = \"<group>\"; };\n\t\t9205E8F01F599FE000927A3F /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = \"<group>\"; };\n\t\t9205E8F11F599FE000927A3F /* core_delegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core_delegate.h; sourceTree = \"<group>\"; };\n\t\t9205E8F31F599FE000927A3F /* cmd_background.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_background.c; sourceTree = \"<group>\"; };\n\t\t9205E8F41F599FE000927A3F /* cmd_background.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_background.h; sourceTree = \"<group>\"; };\n\t\t9205E8F51F599FE000927A3F /* cmd_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_control.c; sourceTree = \"<group>\"; };\n\t\t9205E8F61F599FE000927A3F /* cmd_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_control.h; sourceTree = \"<group>\"; };\n\t\t9205E8F71F599FE000927A3F /* cmd_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_data.c; sourceTree = \"<group>\"; };\n\t\t9205E8F81F599FE000927A3F /* cmd_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_data.h; sourceTree = \"<group>\"; };\n\t\t9205E8F91F599FE000927A3F /* cmd_files.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_files.c; sourceTree = \"<group>\"; };\n\t\t9205E8FA1F599FE000927A3F /* cmd_files.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_files.h; sourceTree = \"<group>\"; };\n\t\t9205E8FB1F599FE000927A3F /* cmd_io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_io.c; sourceTree = \"<group>\"; };\n\t\t9205E8FC1F599FE000927A3F /* cmd_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_io.h; sourceTree = \"<group>\"; };\n\t\t9205E8FD1F599FE000927A3F /* cmd_maths.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_maths.c; sourceTree = \"<group>\"; };\n\t\t9205E8FE1F599FE000927A3F /* cmd_maths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_maths.h; sourceTree = \"<group>\"; };\n\t\t9205E8FF1F599FE000927A3F /* cmd_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_memory.c; sourceTree = \"<group>\"; };\n\t\t9205E9001F599FE000927A3F /* cmd_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_memory.h; sourceTree = \"<group>\"; };\n\t\t9205E9011F599FE000927A3F /* cmd_screen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_screen.c; sourceTree = \"<group>\"; };\n\t\t9205E9021F599FE000927A3F /* cmd_screen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_screen.h; sourceTree = \"<group>\"; };\n\t\t9205E9031F599FE000927A3F /* cmd_sprites.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_sprites.c; sourceTree = \"<group>\"; };\n\t\t9205E9041F599FE000927A3F /* cmd_sprites.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_sprites.h; sourceTree = \"<group>\"; };\n\t\t9205E9051F599FE000927A3F /* cmd_strings.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_strings.c; sourceTree = \"<group>\"; };\n\t\t9205E9061F599FE000927A3F /* cmd_strings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_strings.h; sourceTree = \"<group>\"; };\n\t\t9205E9071F599FE000927A3F /* cmd_text.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_text.c; sourceTree = \"<group>\"; };\n\t\t9205E9081F599FE000927A3F /* cmd_text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_text.h; sourceTree = \"<group>\"; };\n\t\t9205E9091F599FE000927A3F /* cmd_variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_variables.c; sourceTree = \"<group>\"; };\n\t\t9205E90A1F599FE000927A3F /* cmd_variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_variables.h; sourceTree = \"<group>\"; };\n\t\t9205E90B1F599FE000927A3F /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = \"<group>\"; };\n\t\t9205E90C1F599FE000927A3F /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = \"<group>\"; };\n\t\t9205E90D1F599FE000927A3F /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = \"<group>\"; };\n\t\t9205E90E1F599FE000927A3F /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = \"<group>\"; };\n\t\t9205E90F1F599FE000927A3F /* interpreter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interpreter.c; sourceTree = \"<group>\"; };\n\t\t9205E9101F599FE000927A3F /* interpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter.h; sourceTree = \"<group>\"; };\n\t\t9205E9111F599FE000927A3F /* interpreter_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter_config.h; sourceTree = \"<group>\"; };\n\t\t9205E9121F599FE000927A3F /* interpreter_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interpreter_utils.c; sourceTree = \"<group>\"; };\n\t\t9205E9131F599FE000927A3F /* interpreter_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter_utils.h; sourceTree = \"<group>\"; };\n\t\t9205E9141F599FE000927A3F /* labels.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = labels.c; sourceTree = \"<group>\"; };\n\t\t9205E9151F599FE000927A3F /* labels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = labels.h; sourceTree = \"<group>\"; };\n\t\t9205E9161F599FE000927A3F /* rcstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rcstring.c; sourceTree = \"<group>\"; };\n\t\t9205E9171F599FE000927A3F /* rcstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rcstring.h; sourceTree = \"<group>\"; };\n\t\t9205E9181F599FE000927A3F /* token.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = token.c; sourceTree = \"<group>\"; };\n\t\t9205E9191F599FE000927A3F /* token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = token.h; sourceTree = \"<group>\"; };\n\t\t9205E91A1F599FE000927A3F /* value.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = value.c; sourceTree = \"<group>\"; };\n\t\t9205E91B1F599FE000927A3F /* value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = value.h; sourceTree = \"<group>\"; };\n\t\t9205E91C1F599FE000927A3F /* variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = variables.c; sourceTree = \"<group>\"; };\n\t\t9205E91D1F599FE000927A3F /* variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = variables.h; sourceTree = \"<group>\"; };\n\t\t9205E91F1F599FE000927A3F /* default_characters.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = default_characters.c; sourceTree = \"<group>\"; };\n\t\t9205E9201F599FE000927A3F /* default_characters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = default_characters.h; sourceTree = \"<group>\"; };\n\t\t9205E9211F599FE000927A3F /* sprites_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sprites_lib.c; sourceTree = \"<group>\"; };\n\t\t9205E9221F599FE000927A3F /* sprites_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sprites_lib.h; sourceTree = \"<group>\"; };\n\t\t9205E9231F599FE000927A3F /* text_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = text_lib.c; sourceTree = \"<group>\"; };\n\t\t9205E9241F599FE000927A3F /* text_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_lib.h; sourceTree = \"<group>\"; };\n\t\t9205E9261F599FE000927A3F /* audio_chip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = audio_chip.c; sourceTree = \"<group>\"; };\n\t\t9205E9271F599FE000927A3F /* audio_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_chip.h; sourceTree = \"<group>\"; };\n\t\t9205E9281F599FE000927A3F /* io_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_chip.h; sourceTree = \"<group>\"; };\n\t\t9205E9291F599FE000927A3F /* machine.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = machine.c; sourceTree = \"<group>\"; };\n\t\t9205E92A1F599FE000927A3F /* machine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine.h; sourceTree = \"<group>\"; };\n\t\t9205E92B1F599FE000927A3F /* video_chip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = video_chip.c; sourceTree = \"<group>\"; };\n\t\t9205E92C1F599FE000927A3F /* video_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_chip.h; sourceTree = \"<group>\"; };\n\t\t9205E94A1F59A3A400927A3F /* LowResNX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"LowResNX-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t9205E94B1F59A96600927A3F /* program.nx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = program.nx; sourceTree = \"<group>\"; };\n\t\t9205E94D1F59BEE900927A3F /* LowResNXView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LowResNXView.swift; sourceTree = \"<group>\"; };\n\t\t9205E9511F59C57D00927A3F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t9205E9521F59C57D00927A3F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t92521BCE1F5B105E0078A7C7 /* CoreWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreWrapper.swift; sourceTree = \"<group>\"; };\n\t\t9286A70E1F8FB815005C9EC1 /* charsets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = charsets.c; sourceTree = \"<group>\"; };\n\t\t9286A70F1F8FB815005C9EC1 /* tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tokenizer.h; sourceTree = \"<group>\"; };\n\t\t9286A7101F8FB816005C9EC1 /* string_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utils.h; sourceTree = \"<group>\"; };\n\t\t9286A7111F8FB816005C9EC1 /* string_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = string_utils.c; sourceTree = \"<group>\"; };\n\t\t9286A7121F8FB816005C9EC1 /* tokenizer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tokenizer.c; sourceTree = \"<group>\"; };\n\t\t9286A7131F8FB816005C9EC1 /* charsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = charsets.h; sourceTree = \"<group>\"; };\n\t\t9286A7181F8FB84A005C9EC1 /* data_manager.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data_manager.c; sourceTree = \"<group>\"; };\n\t\t9286A7191F8FB84A005C9EC1 /* data_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data_manager.h; sourceTree = \"<group>\"; };\n\t\t9286A71B1F8FCE1A005C9EC1 /* core_delegate.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = core_delegate.c; sourceTree = \"<group>\"; };\n\t\t92AE47D01F6D94A1008BD49C /* overlay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = overlay.c; sourceTree = \"<group>\"; };\n\t\t92AE47D11F6D94A1008BD49C /* overlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = overlay.h; sourceTree = \"<group>\"; };\n\t\t92AE47D21F6D94A1008BD49C /* overlay_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = overlay_data.c; sourceTree = \"<group>\"; };\n\t\t92AE47D31F6D94A1008BD49C /* overlay_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = overlay_data.h; sourceTree = \"<group>\"; };\n\t\t92ED99711FA4F7B80073559B /* startup_sequence.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = startup_sequence.c; sourceTree = \"<group>\"; };\n\t\t92ED99721FA4F7B80073559B /* startup_sequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = startup_sequence.h; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t9205E8D31F599F7300927A3F /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t9205E8CD1F599F7300927A3F = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9205E94B1F59A96600927A3F /* program.nx */,\n\t\t\t\t9205E8EB1F599FE000927A3F /* core */,\n\t\t\t\t9205E8D81F599F7300927A3F /* LowRes NX iOS */,\n\t\t\t\t9205E8D71F599F7300927A3F /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9205E8D71F599F7300927A3F /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9205E8D61F599F7300927A3F /* LowRes NX.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9205E8D81F599F7300927A3F /* LowRes NX iOS */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9205E94A1F59A3A400927A3F /* LowResNX-Bridging-Header.h */,\n\t\t\t\t9205E8D91F599F7300927A3F /* AppDelegate.swift */,\n\t\t\t\t92521BCE1F5B105E0078A7C7 /* CoreWrapper.swift */,\n\t\t\t\t9205E8DB1F599F7300927A3F /* ViewController.swift */,\n\t\t\t\t9205E94D1F59BEE900927A3F /* LowResNXView.swift */,\n\t\t\t\t9205E8DD1F599F7300927A3F /* Main.storyboard */,\n\t\t\t\t9205E8E01F599F7300927A3F /* Assets.xcassets */,\n\t\t\t\t9205E8E21F599F7300927A3F /* LaunchScreen.storyboard */,\n\t\t\t\t9205E8E51F599F7300927A3F /* Info.plist */,\n\t\t\t);\n\t\t\tpath = \"LowRes NX iOS\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9205E8EB1F599FE000927A3F /* core */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9205E8EF1F599FE000927A3F /* core.c */,\n\t\t\t\t9205E8F01F599FE000927A3F /* core.h */,\n\t\t\t\t9205E8F11F599FE000927A3F /* core_delegate.h */,\n\t\t\t\t9286A71B1F8FCE1A005C9EC1 /* core_delegate.c */,\n\t\t\t\t9286A7171F8FB84A005C9EC1 /* datamanager */,\n\t\t\t\t9205E8F21F599FE000927A3F /* interpreter */,\n\t\t\t\t9205E91E1F599FE000927A3F /* libraries */,\n\t\t\t\t9205E9251F599FE000927A3F /* machine */,\n\t\t\t\t9205E8EC1F599FE000927A3F /* accessories */,\n\t\t\t\t92AE47CF1F6D94A1008BD49C /* overlay */,\n\t\t\t);\n\t\t\tname = core;\n\t\t\tpath = ../../core;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9205E8EC1F599FE000927A3F /* accessories */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9205E8ED1F599FE000927A3F /* disk_drive.c */,\n\t\t\t\t9205E8EE1F599FE000927A3F /* disk_drive.h */,\n\t\t\t);\n\t\t\tpath = accessories;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9205E8F21F599FE000927A3F /* interpreter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9286A70E1F8FB815005C9EC1 /* charsets.c */,\n\t\t\t\t9286A7131F8FB816005C9EC1 /* charsets.h */,\n\t\t\t\t9286A7111F8FB816005C9EC1 /* string_utils.c */,\n\t\t\t\t9286A7101F8FB816005C9EC1 /* string_utils.h */,\n\t\t\t\t9286A7121F8FB816005C9EC1 /* tokenizer.c */,\n\t\t\t\t9286A70F1F8FB815005C9EC1 /* tokenizer.h */,\n\t\t\t\t9205E8F31F599FE000927A3F /* cmd_background.c */,\n\t\t\t\t9205E8F41F599FE000927A3F /* cmd_background.h */,\n\t\t\t\t9205E8F51F599FE000927A3F /* cmd_control.c */,\n\t\t\t\t9205E8F61F599FE000927A3F /* cmd_control.h */,\n\t\t\t\t9205E8F71F599FE000927A3F /* cmd_data.c */,\n\t\t\t\t9205E8F81F599FE000927A3F /* cmd_data.h */,\n\t\t\t\t9205E8F91F599FE000927A3F /* cmd_files.c */,\n\t\t\t\t9205E8FA1F599FE000927A3F /* cmd_files.h */,\n\t\t\t\t9205E8FB1F599FE000927A3F /* cmd_io.c */,\n\t\t\t\t9205E8FC1F599FE000927A3F /* cmd_io.h */,\n\t\t\t\t9205E8FD1F599FE000927A3F /* cmd_maths.c */,\n\t\t\t\t9205E8FE1F599FE000927A3F /* cmd_maths.h */,\n\t\t\t\t9205E8FF1F599FE000927A3F /* cmd_memory.c */,\n\t\t\t\t9205E9001F599FE000927A3F /* cmd_memory.h */,\n\t\t\t\t9205E9011F599FE000927A3F /* cmd_screen.c */,\n\t\t\t\t9205E9021F599FE000927A3F /* cmd_screen.h */,\n\t\t\t\t9205E9031F599FE000927A3F /* cmd_sprites.c */,\n\t\t\t\t9205E9041F599FE000927A3F /* cmd_sprites.h */,\n\t\t\t\t9205E9051F599FE000927A3F /* cmd_strings.c */,\n\t\t\t\t9205E9061F599FE000927A3F /* cmd_strings.h */,\n\t\t\t\t9205E9071F599FE000927A3F /* cmd_text.c */,\n\t\t\t\t9205E9081F599FE000927A3F /* cmd_text.h */,\n\t\t\t\t9205E9091F599FE000927A3F /* cmd_variables.c */,\n\t\t\t\t9205E90A1F599FE000927A3F /* cmd_variables.h */,\n\t\t\t\t9205E90B1F599FE000927A3F /* data.c */,\n\t\t\t\t9205E90C1F599FE000927A3F /* data.h */,\n\t\t\t\t9205E90D1F599FE000927A3F /* error.c */,\n\t\t\t\t9205E90E1F599FE000927A3F /* error.h */,\n\t\t\t\t9205E90F1F599FE000927A3F /* interpreter.c */,\n\t\t\t\t9205E9101F599FE000927A3F /* interpreter.h */,\n\t\t\t\t9205E9111F599FE000927A3F /* interpreter_config.h */,\n\t\t\t\t9205E9121F599FE000927A3F /* interpreter_utils.c */,\n\t\t\t\t9205E9131F599FE000927A3F /* interpreter_utils.h */,\n\t\t\t\t9205E9141F599FE000927A3F /* labels.c */,\n\t\t\t\t9205E9151F599FE000927A3F /* labels.h */,\n\t\t\t\t9205E9161F599FE000927A3F /* rcstring.c */,\n\t\t\t\t9205E9171F599FE000927A3F /* rcstring.h */,\n\t\t\t\t9205E9181F599FE000927A3F /* token.c */,\n\t\t\t\t9205E9191F599FE000927A3F /* token.h */,\n\t\t\t\t9205E91A1F599FE000927A3F /* value.c */,\n\t\t\t\t9205E91B1F599FE000927A3F /* value.h */,\n\t\t\t\t9205E91C1F599FE000927A3F /* variables.c */,\n\t\t\t\t9205E91D1F599FE000927A3F /* variables.h */,\n\t\t\t);\n\t\t\tpath = interpreter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9205E91E1F599FE000927A3F /* libraries */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t92ED99711FA4F7B80073559B /* startup_sequence.c */,\n\t\t\t\t92ED99721FA4F7B80073559B /* startup_sequence.h */,\n\t\t\t\t9205E91F1F599FE000927A3F /* default_characters.c */,\n\t\t\t\t9205E9201F599FE000927A3F /* default_characters.h */,\n\t\t\t\t9205E9211F599FE000927A3F /* sprites_lib.c */,\n\t\t\t\t9205E9221F599FE000927A3F /* sprites_lib.h */,\n\t\t\t\t9205E9231F599FE000927A3F /* text_lib.c */,\n\t\t\t\t9205E9241F599FE000927A3F /* text_lib.h */,\n\t\t\t);\n\t\t\tpath = libraries;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9205E9251F599FE000927A3F /* machine */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9205E9261F599FE000927A3F /* audio_chip.c */,\n\t\t\t\t9205E9271F599FE000927A3F /* audio_chip.h */,\n\t\t\t\t9205E9281F599FE000927A3F /* io_chip.h */,\n\t\t\t\t9205E9291F599FE000927A3F /* machine.c */,\n\t\t\t\t9205E92A1F599FE000927A3F /* machine.h */,\n\t\t\t\t9205E92B1F599FE000927A3F /* video_chip.c */,\n\t\t\t\t9205E92C1F599FE000927A3F /* video_chip.h */,\n\t\t\t);\n\t\t\tpath = machine;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9286A7171F8FB84A005C9EC1 /* datamanager */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9286A7181F8FB84A005C9EC1 /* data_manager.c */,\n\t\t\t\t9286A7191F8FB84A005C9EC1 /* data_manager.h */,\n\t\t\t);\n\t\t\tpath = datamanager;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t92AE47CF1F6D94A1008BD49C /* overlay */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t92AE47D01F6D94A1008BD49C /* overlay.c */,\n\t\t\t\t92AE47D11F6D94A1008BD49C /* overlay.h */,\n\t\t\t\t92AE47D21F6D94A1008BD49C /* overlay_data.c */,\n\t\t\t\t92AE47D31F6D94A1008BD49C /* overlay_data.h */,\n\t\t\t);\n\t\t\tpath = overlay;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t9205E8D51F599F7300927A3F /* LowRes NX */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 9205E8E81F599F7300927A3F /* Build configuration list for PBXNativeTarget \"LowRes NX\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t9205E8D21F599F7300927A3F /* Sources */,\n\t\t\t\t9205E8D31F599F7300927A3F /* Frameworks */,\n\t\t\t\t9205E8D41F599F7300927A3F /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"LowRes NX\";\n\t\t\tproductName = \"LowRes NX iOS\";\n\t\t\tproductReference = 9205E8D61F599F7300927A3F /* LowRes NX.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t9205E8CE1F599F7300927A3F /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0830;\n\t\t\t\tLastUpgradeCheck = 0900;\n\t\t\t\tORGANIZATIONNAME = \"Inutilis Software\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t9205E8D51F599F7300927A3F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 8.3.3;\n\t\t\t\t\t\tDevelopmentTeam = BZ4VC623NH;\n\t\t\t\t\t\tLastSwiftMigration = 0900;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 9205E8D11F599F7300927A3F /* Build configuration list for PBXProject \"LowRes NX iOS\" */;\n\t\t\tcompatibilityVersion = \"Xcode 3.2\";\n\t\t\tdevelopmentRegion = English;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 9205E8CD1F599F7300927A3F;\n\t\t\tproductRefGroup = 9205E8D71F599F7300927A3F /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t9205E8D51F599F7300927A3F /* LowRes NX */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t9205E8D41F599F7300927A3F /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t9205E8E41F599F7300927A3F /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t9205E8E11F599F7300927A3F /* Assets.xcassets in Resources */,\n\t\t\t\t9205E94C1F59A96600927A3F /* program.nx in Resources */,\n\t\t\t\t9205E8DF1F599F7300927A3F /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t9205E8D21F599F7300927A3F /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t9205E93E1F599FE000927A3F /* interpreter_utils.c in Sources */,\n\t\t\t\t9205E9361F599FE000927A3F /* cmd_screen.c in Sources */,\n\t\t\t\t9205E9351F599FE000927A3F /* cmd_memory.c in Sources */,\n\t\t\t\t9286A71C1F8FCE1A005C9EC1 /* core_delegate.c in Sources */,\n\t\t\t\t9205E92E1F599FE000927A3F /* core.c in Sources */,\n\t\t\t\t92ED99731FA4F7B90073559B /* startup_sequence.c in Sources */,\n\t\t\t\t9205E8DC1F599F7300927A3F /* ViewController.swift in Sources */,\n\t\t\t\t92AE47D51F6D94A1008BD49C /* overlay_data.c in Sources */,\n\t\t\t\t9205E9321F599FE000927A3F /* cmd_files.c in Sources */,\n\t\t\t\t9205E9461F599FE000927A3F /* text_lib.c in Sources */,\n\t\t\t\t9205E9381F599FE000927A3F /* cmd_strings.c in Sources */,\n\t\t\t\t9286A7151F8FB816005C9EC1 /* string_utils.c in Sources */,\n\t\t\t\t92521BCF1F5B105E0078A7C7 /* CoreWrapper.swift in Sources */,\n\t\t\t\t9205E9451F599FE000927A3F /* sprites_lib.c in Sources */,\n\t\t\t\t9205E9441F599FE000927A3F /* default_characters.c in Sources */,\n\t\t\t\t9205E94E1F59BEE900927A3F /* LowResNXView.swift in Sources */,\n\t\t\t\t9205E9411F599FE000927A3F /* token.c in Sources */,\n\t\t\t\t9205E9401F599FE000927A3F /* rcstring.c in Sources */,\n\t\t\t\t9205E92D1F599FE000927A3F /* disk_drive.c in Sources */,\n\t\t\t\t9205E8DA1F599F7300927A3F /* AppDelegate.swift in Sources */,\n\t\t\t\t9286A71A1F8FB84A005C9EC1 /* data_manager.c in Sources */,\n\t\t\t\t9205E9391F599FE000927A3F /* cmd_text.c in Sources */,\n\t\t\t\t9205E93F1F599FE000927A3F /* labels.c in Sources */,\n\t\t\t\t9205E9331F599FE000927A3F /* cmd_io.c in Sources */,\n\t\t\t\t9286A7161F8FB816005C9EC1 /* tokenizer.c in Sources */,\n\t\t\t\t9286A7141F8FB816005C9EC1 /* charsets.c in Sources */,\n\t\t\t\t9205E9431F599FE000927A3F /* variables.c in Sources */,\n\t\t\t\t9205E9371F599FE000927A3F /* cmd_sprites.c in Sources */,\n\t\t\t\t9205E9341F599FE000927A3F /* cmd_maths.c in Sources */,\n\t\t\t\t9205E93C1F599FE000927A3F /* error.c in Sources */,\n\t\t\t\t9205E93A1F599FE000927A3F /* cmd_variables.c in Sources */,\n\t\t\t\t9205E9301F599FE000927A3F /* cmd_control.c in Sources */,\n\t\t\t\t9205E9491F599FE000927A3F /* video_chip.c in Sources */,\n\t\t\t\t9205E93D1F599FE000927A3F /* interpreter.c in Sources */,\n\t\t\t\t9205E92F1F599FE000927A3F /* cmd_background.c in Sources */,\n\t\t\t\t9205E9421F599FE000927A3F /* value.c in Sources */,\n\t\t\t\t9205E9471F599FE000927A3F /* audio_chip.c in Sources */,\n\t\t\t\t92AE47D41F6D94A1008BD49C /* overlay.c in Sources */,\n\t\t\t\t9205E9481F599FE000927A3F /* machine.c in Sources */,\n\t\t\t\t9205E9311F599FE000927A3F /* cmd_data.c in Sources */,\n\t\t\t\t9205E93B1F599FE000927A3F /* data.c in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t9205E8DD1F599F7300927A3F /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t9205E9511F59C57D00927A3F /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9205E8E21F599F7300927A3F /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t9205E9521F59C57D00927A3F /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t9205E8E61F599F7300927A3F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t9205E8E71F599F7300927A3F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Owholemodule\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t9205E8E91F599F7300927A3F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tDEVELOPMENT_TEAM = BZ4VC623NH;\n\t\t\t\tINFOPLIST_FILE = \"LowRes NX iOS/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.inutilis.ios.LowRes-NX\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"LowRes NX iOS/LowResNX-Bridging-Header.h\";\n\t\t\t\tSWIFT_SWIFT3_OBJC_INFERENCE = Default;\n\t\t\t\tSWIFT_VERSION = 4.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t9205E8EA1F599F7300927A3F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tDEVELOPMENT_TEAM = BZ4VC623NH;\n\t\t\t\tINFOPLIST_FILE = \"LowRes NX iOS/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.inutilis.ios.LowRes-NX\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"LowRes NX iOS/LowResNX-Bridging-Header.h\";\n\t\t\t\tSWIFT_SWIFT3_OBJC_INFERENCE = Default;\n\t\t\t\tSWIFT_VERSION = 4.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t9205E8D11F599F7300927A3F /* Build configuration list for PBXProject \"LowRes NX iOS\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t9205E8E61F599F7300927A3F /* Debug */,\n\t\t\t\t9205E8E71F599F7300927A3F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t9205E8E81F599F7300927A3F /* Build configuration list for PBXNativeTarget \"LowRes NX\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t9205E8E91F599F7300927A3F /* Debug */,\n\t\t\t\t9205E8EA1F599F7300927A3F /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 9205E8CE1F599F7300927A3F /* Project object */;\n}\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:LowRes NX iOS.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "platform/iOS/LowRes NX iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "platform/iOS/program.nx",
    "content": "RANDOMIZE TIMER\n\n'VALUES FOR 16 ALIENS\nDIM ALIENS(15,3)\n' 0 TYPE (0=DISABLED)\n' 1 TICK\n' 2 START Y\n' 3 HITS\n\nBG 0\nBG COPY 0,0,32,16 TO 0,0\nBG 1\nBG COPY 0,16,32,16 TO 0,0\n\nGAMEPAD 1\n\nON RASTER GOSUB RASTERFX\n\n'INIT SPRITES\nSPRITE.A 0,(4,,,,1)\nSPRITE.A 1,(5,,,,)\nFOR I=2 TO 9\n  SPRITE.A I,(5,,,,)\nNEXT I\n\n'INIT VARIABLES\nSCORE=0\nPX=64\nPY=80\nBULLET=0\nALIEN=0\n\n'HUD\nBG 0\nFONT ,(0,,,1,)\nTEXT 0,0,\"BETA\"\n\n'GAME LOOP\nDO\n  TICK=TICK+1\n\n  'SPAWN ALIENS\n  IF TICK MOD 60=0 THEN\n    ALIENS(ALIEN,0)=INT(RND*4)+1\n    ALIENS(ALIEN,1)=0\n    ALIENS(ALIEN,2)=32+RND*96\n    ALIENS(ALIEN,3)=0\n    ALIEN=(ALIEN+1) MOD 16\n  END IF\n\n  'UPDATE ALIENS\n  FOR I=0 TO 15\n    ATYPE=ALIENS(I,0)\n    IF ATYPE>0 THEN\n      ALIENS(I,1)=ALIENS(I,1)+1\n      ATICK=ALIENS(I,1)\n      X=192-ATICK\n      Y=ALIENS(I,2)\n      IF X>0 THEN\n        IF ATYPE MOD 2=0 THEN PAL=6 ELSE PAL=7\n        IF ATYPE<=2 THEN\n          SIZE=0\n          FRAME=10+INT((ATICK MOD 12)/6)\n          Y=Y+SIN(ATICK/10)*16\n        ELSE\n          SIZE=1\n          FRAME=6+INT((ATICK MOD 16)/8)*2\n          Y=Y+SIN(ATICK/30)*32\n        END IF\n        SPRITE.A 10+I,(PAL,,,,SIZE)\n        SPRITE 10+I,X,Y,FRAME\n        IF SPRITE HIT(10+I,2 TO 9) THEN\n          SPRITE HIT,0,0,\n          SPRITE.A 10+I,(3,,,,)\n          ALIENS(I,3)=ALIENS(I,3)+1\n          SCORE=SCORE+20\n        END IF\n      ELSE\n        'RESET\n        SPRITE 10+I,0,0,\n        ALIENS(I,0)=0\n      END IF\n    END IF\n  NEXT I\n\n  'UPDATE BULLETS\n  FOR I=2 TO 9\n    IF SPRITE.Y(I)>0 THEN\n      SPRITE I,SPRITE.X(I)+6,,\n      IF SPRITE.X(I)>192 THEN SPRITE I,0,0,\n    END IF\n  NEXT I\n\n  'PLAYER CONTROL\n  IF UP(0) AND PY>32 THEN PY=PY-2\n  IF DOWN(0) AND PY<144 THEN PY=PY+2\n  IF LEFT(0) AND PX>40 THEN PX=PX-2\n  IF RIGHT(0) AND PX<160 THEN PX=PX+2\n\n  'PLAYER SPRITE\n  SPRITE 0,PX,PY,1\n  SPRITE 1,PX-8,PY+5,19+INT((TICK MOD 8)/4)\n\n  'SHOOT\n  IF BUTTON TAP(0,0) OR BUTTON TAP(0,1) THEN\n    SPRITE BULLET+2,PX+8,PY+11,3\n    BULLET=(BULLET+1) MOD 8\n  END IF\n\n  'HUD\n  NUMBER 15,0,SCORE,5\n\n  WAIT VBL\nLOOP\n\n\nRASTERFX:\n  'STARS AND FOREGROUND HILLS\n  IF RASTER=0 THEN\n    DISPLAY 1,TICK/3,0\n  ELSE IF RASTER=80 THEN\n    DISPLAY 1,TICK*2,0\n  END IF\n\n  'STATUS BAR AND PLANET SURFACE\n  IF RASTER=0 THEN\n    DISPLAY 0,0,0\n  ELSE IF RASTER=8 THEN\n    DISPLAY 0,TICK,0\n  ELSE IF RASTER=80 THEN\n    DISPLAY 0,TICK*5/4,0\n  ELSE IF RASTER=96 THEN\n    DISPLAY 0,TICK*6/4,0\n  ELSE IF RASTER=112 THEN\n    DISPLAY 0,TICK*7/4,0\n  END IF\nRETURN\n\n\n#1:MAIN PALETTES\n00 3F 1B 05 00 26 11 01 00 3B 26 12 00 3F 3E 39\n00 1F 2A 15 00 3C 30 06 00 3F 31 21 00 3F 16 12\n\n#2:MAIN CHARACTERS\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 40 60 00 78 FF 54 00 00 40 60 70 78 FF FF\n00 00 00 00 00 00 FC 02 00 00 00 00 00 00 FC FE\n00 B3 00 00 00 00 00 00 02 BC 02 00 00 00 00 00\n90 60 60 90 00 00 00 00 F0 90 90 F0 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 04 10 30 40 0C 8E 0E 00 07 1F 2F 7F 73 F5 F5\n00 20 08 04 02 60 E1 E0 00 E0 F8 FC FE 9E 5F 5F\n04 10 28 40 0C 8E 0E 06 07 1F 37 7F 73 F5 F5 F9\n20 08 04 02 60 E1 E0 C0 E0 F8 FC FE 9E 5F 5F 3F\n00 42 81 66 00 00 66 42 00 7E FF BD FF FF 66 42\n42 81 66 66 00 00 66 24 7E FF BD BD FF 7E 66 24\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\nA8 FF 00 78 02 60 40 00 FF FF 7C 78 7C 60 40 00\n7F BD FA 7C 00 00 00 00 FF C3 86 7C 00 00 00 00\n10 04 09 A3 09 04 10 00 10 06 0E BC 0E 06 10 00\n40 12 04 13 04 12 40 00 40 12 07 1C 07 12 40 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n04 00 80 40 3C 38 30 18 FB FF FF 7F 3C 38 30 18\n40 00 01 02 3C 1C 0C 18 BF FF FF FE 3C 1C 0C 18\n80 00 40 20 3C 1C 0E 02 FF 7F 7F 3F 3C 1C 0E 02\n01 00 02 04 3C 38 70 40 FF FE FE FC 3C 38 70 40\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 40 00 00 00 00 02 00 00 00 00 00 00 00 02 00\n00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00\n00 00 08 00 2A 00 08 00 00 00 08 08 36 08 08 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00\n00 00 00 00 00 00 04 00 00 00 00 00 00 00 04 00\n00 48 30 30 48 00 00 00 00 78 48 48 78 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF\n00 00 7E C3 C3 7E 00 00 FF FF FF FC FC 81 FF FF\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 08 22 00 00 00 00 00 18 76 DF\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 38 44 38 00 FF FF FF FF FF FB C7 FF\n00 00 1C 0E 00 00 00 00 FF FF FF F1 FF FF FF FF\n00 00 01 04 01 02 16 4C 01 03 06 0B 1E 3D 69 B3\n00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF\n00 80 40 60 B0 10 28 04 80 C0 E0 F0 F8 FC FE FF\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n18 3C 3C 3C 3C 18 3C 18 18 24 24 24 24 18 24 18\n6C FE FE 7E 24 00 00 00 6C 92 92 5A 24 00 00 00\n24 7E FF 7E 7E FF 7E 24 24 5A 81 5A 5A 81 5A 24\n08 3E 7F 7E 3F 7F 3E 08 08 36 41 46 31 41 36 08\n62 F7 FE 7C 3E 7F EF 46 62 95 9A 74 2E 59 A9 46\n1C 3E 7E 7E FF FE 7F 3A 1C 22 4A 46 91 9A 45 3A\n18 3C 3C 78 30 00 00 00 18 24 24 48 30 00 00 00\n0C 1E 3C 78 78 3C 1E 0C 0C 12 24 48 48 24 12 0C\n30 78 3C 1E 1E 3C 78 30 30 48 24 12 12 24 48 30\n00 24 7E 7E FF 7E 7E 24 00 24 5A 66 81 66 5A 24\n00 18 3C 7E FF 7E 3C 18 00 18 24 66 81 66 24 18\n00 00 00 18 3C 3C 78 30 00 00 00 18 24 24 48 30\n00 00 00 7E FF 7E 00 00 00 00 00 7E 81 7E 00 00\n00 00 00 00 18 3C 3C 18 00 00 00 00 18 24 24 18\n06 0F 1E 3C 78 F0 E0 40 06 09 12 24 48 90 A0 40\n3C 7E FF FF FF FF 7E 3C 3C 42 99 91 89 99 42 3C\n18 3C 7C 3C 3C 7E FF 7E 18 24 44 24 24 66 81 7E\n3C 7E FF 7E 3C 7E FF 7E 3C 42 99 72 24 4E 81 7E\n3C 7E FF 7E 6F FF 7E 3C 3C 42 99 72 69 99 42 3C\n66 FF FF FF 7F 0F 0F 06 66 99 99 81 79 09 09 06\n7E FF FE FE 7F 7F FE 7C 7E 81 9E 82 79 79 82 7C\n1C 3E 7C FE FF FF 7E 3C 1C 22 4C 82 99 99 42 3C\n7E FF 7F 1E 3C 78 78 30 7E 81 79 12 24 48 48 30\n3C 7E FF 7E FF FF 7E 3C 3C 42 99 42 99 99 42 3C\n3C 7E FF 7F 7F FF 7E 3C 3C 42 99 41 79 99 42 3C\n00 00 18 3C 18 3C 18 00 00 00 18 24 18 24 18 00\n00 00 18 3C 18 3C 78 30 00 00 18 24 18 24 48 30\n00 0C 1E 3C 78 3C 1E 0C 00 0C 12 24 48 24 12 0C\n00 00 7E FF 7E FF 7E 00 00 00 7E 81 7E 81 7E 00\n00 30 78 3C 1E 3C 78 30 00 30 48 24 12 24 48 30\n3C 7E FF 7E 3C 18 3C 18 3C 42 99 72 24 18 24 18\n3C 7E FF FF FF FE 7E 3C 3C 42 99 91 91 9E 42 3C\n18 3C 7E FF FF FF FF 66 18 24 42 99 81 99 99 66\n7C FE FF FE FF FF FE 7C 7C 82 99 82 99 99 82 7C\n3C 7E FF F6 F6 FF 7E 3C 3C 42 99 96 96 99 42 3C\n78 FC FE FF FF FE FC 78 78 84 92 99 99 92 84 78\n7E FF FE FC F8 FE FF 7E 7E 81 9E 84 98 9E 81 7E\n7E FF FE FC F8 F0 F0 60 7E 81 9E 84 98 90 90 60\n3C 7E FE FF FF FF 7E 3C 3C 42 9E 91 99 99 42 3C\n66 FF FF FF FF FF FF 66 66 99 99 81 99 99 99 66\n3C 7E 3C 3C 3C 3C 7E 3C 3C 42 24 24 24 24 42 3C\n1E 3F 1F 0F 6F FF 7E 3C 1E 21 19 09 69 99 42 3C\n66 FF FE FC FC FE FF 66 66 99 92 84 84 92 99 66\n60 F0 F0 F0 F0 FE FF 7E 60 90 90 90 90 9E 81 7E\n42 E7 FF FF FF FF FF 66 42 A5 99 81 81 99 99 66\n66 FF FF FF FF FF FF 66 66 99 89 81 91 99 99 66\n3C 7E FF FF FF FF 7E 3C 3C 42 99 99 99 99 42 3C\n7C FE FF FE FC F0 F0 60 7C 82 99 82 9C 90 90 60\n3C 7E FF FF FF FE 7F 3E 3C 42 99 99 95 92 41 3E\n7C FE FF FE FC FE FF 66 7C 82 99 82 84 92 99 66\n3E 7F FE 7E 3F 7F FE 7C 3E 41 9E 42 39 79 82 7C\n7E FF 7E 3C 3C 3C 3C 18 7E 81 66 24 24 24 24 18\n66 FF FF FF FF FF 7E 3C 66 99 99 99 99 99 42 3C\n66 FF FF FF FF 7E 3C 18 66 99 99 99 99 42 24 18\n66 FF FF FF FF FF E7 42 66 99 99 81 81 99 A5 42\n66 FF 7E 3C 7E FF FF 66 66 99 42 24 42 99 99 66\n66 FF FF 7E 3C 3C 3C 18 66 99 99 42 24 24 24 18\n7E FF 7E 3C 78 FE FF 7E 7E 81 72 24 48 9E 81 7E\n3C 7E 7C 78 78 7C 7E 3C 3C 42 4C 48 48 4C 42 3C\n60 F0 78 3C 1E 0F 07 02 60 90 48 24 12 09 05 02\n3C 7E 3E 1E 1E 3E 7E 3C 3C 42 32 12 12 32 42 3C\n18 3C 7E FF 66 00 00 00 18 24 42 99 66 00 00 00\n00 00 00 00 00 7E FF 7E 00 00 00 00 00 7E 81 7E\n\n#3:MAIN BG\n00 00 00 00 00 00 00 21 00 21 00 21 00 21 00 00\n00 21 00 21 00 21 00 00 00 00 00 00 00 00 00 21\n00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 00\n00 00 00 21 00 21 00 21 00 21 00 21 00 21 00 21\n00 00 00 00 00 00 00 21 00 21 00 00 00 21 00 00\n00 21 00 21 00 21 00 00 00 00 00 00 00 00 00 21\n00 21 00 21 00 00 00 21 00 00 00 21 00 00 00 21\n00 00 00 00 00 21 00 21 00 21 00 21 00 21 00 21\n00 21 00 00 00 00 00 00 00 21 00 21 00 21 00 21\n00 21 00 21 00 21 00 00 00 21 00 00 00 21 00 21\n00 21 00 00 00 21 00 21 00 00 00 21 00 21 00 21\n00 21 00 00 00 00 00 21 00 00 00 21 00 00 00 21\n00 00 00 21 00 00 00 00 00 00 00 00 00 00 00 00\n00 21 00 21 00 21 00 00 00 00 00 21 00 00 00 21\n00 00 00 00 00 21 00 21 00 00 00 00 00 00 00 00\n00 00 00 00 00 21 00 21 00 21 00 00 00 21 00 21\n00 00 00 00 00 00 00 21 00 21 00 21 00 21 00 21\n00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 21\n00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 21\n00 21 00 00 00 21 00 21 00 21 00 00 00 00 00 00\n00 00 00 00 00 00 00 21 00 21 00 21 00 21 00 00\n00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 00\n00 00 00 21 00 21 00 21 00 00 00 21 00 21 00 21\n00 21 00 00 00 00 00 21 00 21 00 21 00 00 00 00\n00 21 00 21 00 21 00 00 00 00 00 21 00 21 00 21\n00 00 00 00 00 21 00 21 00 21 00 21 00 00 00 00\n00 21 00 21 00 21 00 21 00 00 00 21 00 21 00 21\n00 21 00 21 00 00 00 21 00 21 00 21 00 21 00 21\n00 21 00 21 00 21 00 21 00 21 00 21 00 21 00 21\n00 21 00 21 62 01 63 01 64 01 00 21 00 21 00 21\n00 00 00 21 00 21 00 21 00 21 00 21 00 00 62 01\n63 01 64 01 00 21 00 21 00 21 00 21 00 21 00 01\n00 01 00 01 62 01 63 01 64 01 00 01 62 01 00 01\n64 01 63 01 72 01 73 01 74 01 00 01 00 01 62 01\n63 01 64 01 00 21 00 21 00 01 00 01 63 01 72 01\n73 01 74 01 63 01 00 21 00 21 62 01 63 01 64 01\n63 01 63 01 72 01 73 01 74 01 63 01 63 01 63 01\n72 01 73 01 73 01 73 01 73 01 74 01 63 01 72 01\n73 01 74 01 63 01 63 01 63 01 72 01 73 01 73 01\n73 01 73 01 73 01 74 01 63 01 72 01 73 01 74 01\n60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01\n71 01 60 01 73 01 73 01 73 01 60 01 60 01 60 01\n71 01 60 01 60 01 60 01 60 01 60 01 71 01 60 01\n60 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01\n70 01 60 01 60 01 71 01 70 01 71 01 70 01 60 01\n71 01 60 01 70 01 60 01 70 01 71 01 70 01 60 01\n60 01 71 01 60 01 60 01 60 01 71 01 60 01 60 01\n71 01 60 01 60 01 71 01 60 01 60 01 71 01 60 01\n60 01 60 01 60 01 71 01 60 01 60 01 60 01 60 01\n60 01 60 01 60 01 60 01 60 01 60 01 60 01 70 01\n61 01 60 01 60 01 71 01 70 01 60 01 60 01 60 01\n70 01 60 01 60 01 60 01 60 01 60 01 60 01 60 01\n70 01 71 01 60 01 60 01 60 01 70 01 60 01 61 01\n70 01 71 01 60 01 60 01 70 01 60 01 60 01 60 01\n70 01 60 01 71 01 60 01 60 01 60 01 61 01 60 01\n60 01 70 01 60 01 71 01 70 01 71 01 70 01 71 01\n60 01 60 01 70 01 60 01 60 01 60 01 60 01 70 01\n60 01 60 01 60 01 61 01 60 01 70 01 60 01 61 01\n60 01 60 01 60 01 70 01 60 01 71 01 60 01 60 01\n60 01 61 01 60 01 70 01 60 01 60 01 60 01 61 01\n70 01 60 01 60 01 61 01 60 01 70 01 60 01 60 01\n60 01 70 01 60 01 60 01 60 01 71 01 70 01 71 01\n60 01 71 01 70 01 71 01 70 01 60 01 70 01 71 01\n70 01 60 01 60 01 60 01 61 01 60 01 70 01 71 01\n40 00 41 00 00 00 00 00 00 00 00 00 51 00 00 00\n00 00 00 00 50 00 00 00 00 00 00 00 50 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 50 00 00 00 00 00 40 00 41 00\n50 00 51 00 00 00 40 00 41 00 00 00 00 00 00 00\n00 00 00 00 00 00 51 00 00 00 00 00 00 00 51 00\n00 00 00 00 40 00 41 00 00 00 00 00 00 00 40 00\n41 00 00 00 00 00 00 00 00 00 00 00 50 00 42 00\n00 00 51 00 00 00 50 00 51 00 00 00 50 00 00 00\n42 00 41 00 00 00 00 00 40 00 41 00 00 00 00 00\n51 00 00 00 50 00 51 00 00 00 00 00 00 00 50 00\n51 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 51 00 00 00 51 00 00 00\n50 00 51 00 50 00 00 00 50 00 51 00 00 00 51 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 51 00 50 00 51 00 00 00 51 00 00 00\n00 00 40 00 41 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 00\n00 00 41 00 00 00 00 00 00 00 51 00 40 00 41 00\n00 00 00 00 52 01 00 00 00 00 00 00 00 00 00 00\n00 00 50 00 51 00 50 00 00 00 40 00 50 00 00 00\n00 00 40 00 41 00 41 00 00 00 00 00 00 00 00 00\n51 00 00 00 00 00 50 00 00 00 00 00 50 00 51 00\n00 00 00 00 00 00 40 00 41 00 00 00 00 00 00 00\n00 00 00 00 50 00 00 00 00 00 50 00 51 00 00 00\n00 00 50 00 51 00 00 00 00 00 40 00 50 00 00 00\n50 00 00 00 51 00 00 00 00 00 50 00 00 00 00 00\n00 00 51 00 00 00 50 00 51 00 00 00 00 00 00 00\n00 00 00 00 50 00 41 00 00 00 00 00 00 00 40 00\n41 00 00 00 00 00 00 00 00 00 50 00 51 00 00 00\n00 00 00 00 00 00 00 00 51 00 00 00 00 00 51 00\n00 00 50 00 00 00 00 00 00 00 40 00 41 00 00 00\n50 00 00 00 50 00 42 00 00 00 51 00 62 01 00 01\n64 01 00 00 52 01 51 00 00 00 51 00 50 00 51 00\n00 00 40 00 41 00 00 00 00 00 42 00 00 00 00 00\n00 00 00 00 00 00 51 00 00 00 50 00 51 00 00 00\n51 00 00 00 00 00 00 00 62 01 62 01 00 01 00 01\n00 01 00 00 51 00 00 00 00 00 00 00 00 00 00 00\n00 00 50 00 51 00 00 00 51 00 00 00 50 00 00 00\n00 00 00 00 51 00 00 00 51 00 00 00 00 00 00 00\n40 00 62 01 62 01 62 01 00 01 00 01 00 01 00 01\n50 00 00 00 00 00 00 00 62 22 00 02 64 22 51 00\n00 00 51 00 00 00 00 00 00 00 00 00 00 00 00 00\n40 00 41 00 00 00 00 00 00 00 00 00 00 00 00 00\n50 00 00 01 00 01 00 01 00 01 00 01 41 00 00 00\n00 00 00 01 00 01 00 01 00 02 00 02 64 02 00 01\n00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 01\n00 01 00 01 00 01 00 01 00 01 00 01 00 01 00 00\n00 00 00 00 00 00 00 00 00 00 00 01 00 21 00 20\n00 20 00 22 00 02 00 22 00 22 63 22 00 22 00 02\n00 00 00 00 00 00 00 01 00 01 00 01 00 01 00 01\n00 00 00 00 00 00 00 01 00 01 00 01 00 01 00 00\n00 00 00 00 62 21 63 22 64 22 00 20 00 00 00 02\n00 02 62 02 00 02 00 22 72 22 73 22 74 22 00 22\n00 02 00 01 62 22 63 22 64 22 65 22 00 00 00 00\n00 00 00 00 00 00 00 00 62 22 63 22 64 01 00 00\n00 00 00 02 72 22 73 22 74 22 00 20 00 21 00 22\n00 02 00 22 63 22 72 22 70 22 73 22 73 22 74 22\n00 22 00 02 72 22 73 22 74 22 63 22 63 22 64 02\n00 00 62 02 63 22 63 22 72 22 73 22 74 22 00 22\n00 22 72 22 73 22 71 22 73 22 74 22 00 22 00 02\n00 22 72 22 73 22 73 22 73 22 61 22 73 22 71 22\n74 22 72 22 73 22 73 22 73 22 73 22 73 22 74 22\n00 20 72 22 73 22 73 22 71 22 73 22 73 22 74 22\n\n"
  },
  {
    "path": "platform/macOS/LowRes NX macOS/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 16.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 16@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 32.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 64.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 128.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 256-1.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 256.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 512-1.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 512.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"Mac App Icon 512@2x.png\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "platform/macOS/LowRes NX macOS/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "platform/macOS/LowRes NX macOS/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDocumentTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t<array>\n\t\t\t\t<string>nx</string>\n\t\t\t</array>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>LowRes NX Program</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>LSTypeIsPackage</key>\n\t\t\t<integer>0</integer>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>Copyright © 2017-2021 Timo Kloss.\nThis program is released under the zlib License.</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n\t<key>SDL_FILESYSTEM_BASE_DIR_TYPE</key>\n\t<string>parent</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "platform/macOS/LowRes NX macOS/LowRes_NX_macOS_SDL.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "platform/macOS/LowRes NX macOS.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t921E0F152104BD7100F3C512 /* settings.c in Sources */ = {isa = PBXBuildFile; fileRef = 921E0F142104BD7100F3C512 /* settings.c */; };\n\t\t921F986620DAD4DF0052F233 /* boot_intro.c in Sources */ = {isa = PBXBuildFile; fileRef = 921F986520DAD4DF0052F233 /* boot_intro.c */; };\n\t\t9251FDE621259EDE003915D9 /* cmd_audio.c in Sources */ = {isa = PBXBuildFile; fileRef = 9251FDE521259EDE003915D9 /* cmd_audio.c */; };\n\t\t9258F207218C49C000CE81D6 /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9258F206218C49C000CE81D6 /* utils.c */; };\n\t\t925D482D2121FF76003F1B0D /* audio_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 925D482C2121FF76003F1B0D /* audio_lib.c */; };\n\t\t925F6BEE25039D12005E1179 /* core_stats.c in Sources */ = {isa = PBXBuildFile; fileRef = 925F6BEC25039D11005E1179 /* core_stats.c */; };\n\t\t9284722B20F9F4FA00B82653 /* dev_menu.c in Sources */ = {isa = PBXBuildFile; fileRef = 9284722A20F9F4FA00B82653 /* dev_menu.c */; };\n\t\t9287EAC920D85E4E000BBEB1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9287EAC820D85E4E000BBEB1 /* Assets.xcassets */; };\n\t\t9287EB2D20D85EEB000BBEB1 /* cmd_variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EADC20D85EEB000BBEB1 /* cmd_variables.c */; };\n\t\t9287EB2E20D85EEB000BBEB1 /* cmd_data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EADD20D85EEB000BBEB1 /* cmd_data.c */; };\n\t\t9287EB2F20D85EEB000BBEB1 /* rcstring.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EADF20D85EEB000BBEB1 /* rcstring.c */; };\n\t\t9287EB3020D85EEB000BBEB1 /* cmd_background.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE120D85EEB000BBEB1 /* cmd_background.c */; };\n\t\t9287EB3120D85EEB000BBEB1 /* cmd_files.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE220D85EEB000BBEB1 /* cmd_files.c */; };\n\t\t9287EB3220D85EEB000BBEB1 /* cmd_control.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE420D85EEB000BBEB1 /* cmd_control.c */; };\n\t\t9287EB3320D85EEB000BBEB1 /* variables.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE620D85EEB000BBEB1 /* variables.c */; };\n\t\t9287EB3420D85EEB000BBEB1 /* token.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAE920D85EEB000BBEB1 /* token.c */; };\n\t\t9287EB3520D85EEB000BBEB1 /* cmd_maths.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAEA20D85EEB000BBEB1 /* cmd_maths.c */; };\n\t\t9287EB3620D85EEB000BBEB1 /* charsets.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAEE20D85EEB000BBEB1 /* charsets.c */; };\n\t\t9287EB3720D85EEB000BBEB1 /* cmd_text.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAEF20D85EEB000BBEB1 /* cmd_text.c */; };\n\t\t9287EB3820D85EEB000BBEB1 /* interpreter_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF020D85EEB000BBEB1 /* interpreter_utils.c */; };\n\t\t9287EB3920D85EEB000BBEB1 /* labels.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF220D85EEB000BBEB1 /* labels.c */; };\n\t\t9287EB3A20D85EEB000BBEB1 /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF520D85EEB000BBEB1 /* error.c */; };\n\t\t9287EB3B20D85EEB000BBEB1 /* cmd_memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF620D85EEB000BBEB1 /* cmd_memory.c */; };\n\t\t9287EB3C20D85EEB000BBEB1 /* cmd_strings.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAF720D85EEB000BBEB1 /* cmd_strings.c */; };\n\t\t9287EB3D20D85EEB000BBEB1 /* string_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAFC20D85EEB000BBEB1 /* string_utils.c */; };\n\t\t9287EB3E20D85EEB000BBEB1 /* cmd_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAFD20D85EEB000BBEB1 /* cmd_subs.c */; };\n\t\t9287EB3F20D85EEB000BBEB1 /* cmd_io.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EAFF20D85EEB000BBEB1 /* cmd_io.c */; };\n\t\t9287EB4020D85EEB000BBEB1 /* interpreter.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0020D85EEB000BBEB1 /* interpreter.c */; };\n\t\t9287EB4120D85EEB000BBEB1 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0220D85EEB000BBEB1 /* data.c */; };\n\t\t9287EB4220D85EEB000BBEB1 /* cmd_sprites.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0420D85EEB000BBEB1 /* cmd_sprites.c */; };\n\t\t9287EB4320D85EEB000BBEB1 /* value.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0720D85EEB000BBEB1 /* value.c */; };\n\t\t9287EB4420D85EEB000BBEB1 /* cmd_screen.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0820D85EEB000BBEB1 /* cmd_screen.c */; };\n\t\t9287EB4520D85EEB000BBEB1 /* tokenizer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0B20D85EEB000BBEB1 /* tokenizer.c */; };\n\t\t9287EB4620D85EEB000BBEB1 /* disk_drive.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB0F20D85EEB000BBEB1 /* disk_drive.c */; };\n\t\t9287EB4720D85EEB000BBEB1 /* data_manager.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1220D85EEB000BBEB1 /* data_manager.c */; };\n\t\t9287EB4820D85EEB000BBEB1 /* sprites_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1520D85EEB000BBEB1 /* sprites_lib.c */; };\n\t\t9287EB4920D85EEB000BBEB1 /* default_characters.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1A20D85EEB000BBEB1 /* default_characters.c */; };\n\t\t9287EB4A20D85EEB000BBEB1 /* startup_sequence.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1B20D85EEB000BBEB1 /* startup_sequence.c */; };\n\t\t9287EB4B20D85EEB000BBEB1 /* text_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1C20D85EEB000BBEB1 /* text_lib.c */; };\n\t\t9287EB4C20D85EEB000BBEB1 /* core.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB1E20D85EEB000BBEB1 /* core.c */; };\n\t\t9287EB4D20D85EEB000BBEB1 /* audio_chip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2220D85EEB000BBEB1 /* audio_chip.c */; };\n\t\t9287EB4E20D85EEB000BBEB1 /* video_chip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2420D85EEB000BBEB1 /* video_chip.c */; };\n\t\t9287EB4F20D85EEB000BBEB1 /* machine.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2520D85EEB000BBEB1 /* machine.c */; };\n\t\t9287EB5020D85EEB000BBEB1 /* overlay_data.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2820D85EEB000BBEB1 /* overlay_data.c */; };\n\t\t9287EB5120D85EEB000BBEB1 /* overlay.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2A20D85EEB000BBEB1 /* overlay.c */; };\n\t\t9287EB5220D85EEB000BBEB1 /* core_delegate.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB2C20D85EEB000BBEB1 /* core_delegate.c */; };\n\t\t9287EB5620D85EF7000BBEB1 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 9287EB5520D85EF7000BBEB1 /* main.c */; };\n\t\t9287EB5920D85F43000BBEB1 /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9287EB5820D85F43000BBEB1 /* SDL2.framework */; };\n\t\t9289C403212866FD009BE093 /* SDL2.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9287EB5820D85F43000BBEB1 /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\t928CA2E62189DC370085125F /* runner.c in Sources */ = {isa = PBXBuildFile; fileRef = 928CA2E52189DC370085125F /* runner.c */; };\n\t\t92D708942181C0ED00F40043 /* screenshot.c in Sources */ = {isa = PBXBuildFile; fileRef = 92D708932181C0ED00F40043 /* screenshot.c */; };\n\t\t92D7089E2181E0A400F40043 /* system_paths.c in Sources */ = {isa = PBXBuildFile; fileRef = 92D7089D2181E0A400F40043 /* system_paths.c */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9289C402212866E8009BE093 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\t9289C403212866FD009BE093 /* SDL2.framework in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t921E0F132104BD7100F3C512 /* settings.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = settings.h; sourceTree = \"<group>\"; };\n\t\t921E0F142104BD7100F3C512 /* settings.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = settings.c; sourceTree = \"<group>\"; };\n\t\t921F986420DAD4DF0052F233 /* boot_intro.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = boot_intro.h; sourceTree = \"<group>\"; };\n\t\t921F986520DAD4DF0052F233 /* boot_intro.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = boot_intro.c; sourceTree = \"<group>\"; };\n\t\t9251FDE421259EDE003915D9 /* cmd_audio.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cmd_audio.h; sourceTree = \"<group>\"; };\n\t\t9251FDE521259EDE003915D9 /* cmd_audio.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cmd_audio.c; sourceTree = \"<group>\"; };\n\t\t9258F205218C49C000CE81D6 /* utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = \"<group>\"; };\n\t\t9258F206218C49C000CE81D6 /* utils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = \"<group>\"; };\n\t\t925D482B2121FF76003F1B0D /* audio_lib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = audio_lib.h; sourceTree = \"<group>\"; };\n\t\t925D482C2121FF76003F1B0D /* audio_lib.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = audio_lib.c; sourceTree = \"<group>\"; };\n\t\t925F6BEC25039D11005E1179 /* core_stats.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core_stats.c; sourceTree = \"<group>\"; };\n\t\t925F6BED25039D12005E1179 /* core_stats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core_stats.h; sourceTree = \"<group>\"; };\n\t\t92648F1625BAD73D008559AA /* libretro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libretro.h; sourceTree = \"<group>\"; };\n\t\t92648F6425BAE851008559AA /* libretro_main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libretro_main.h; sourceTree = \"<group>\"; };\n\t\t92648F6525BAE851008559AA /* libretro_main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = libretro_main.c; sourceTree = \"<group>\"; };\n\t\t9284722920F9F4FA00B82653 /* dev_menu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dev_menu.h; sourceTree = \"<group>\"; };\n\t\t9284722A20F9F4FA00B82653 /* dev_menu.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = dev_menu.c; sourceTree = \"<group>\"; };\n\t\t9284722C20F9FA5900B82653 /* dev_menu_data.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dev_menu_data.h; sourceTree = \"<group>\"; };\n\t\t9287EAC220D85E4D000BBEB1 /* LowRes NX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"LowRes NX.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t9287EAC820D85E4E000BBEB1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t9287EACD20D85E4E000BBEB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t9287EAD020D85E4E000BBEB1 /* LowRes_NX_macOS_SDL.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LowRes_NX_macOS_SDL.entitlements; sourceTree = \"<group>\"; };\n\t\t9287EADA20D85EEB000BBEB1 /* cmd_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_memory.h; sourceTree = \"<group>\"; };\n\t\t9287EADB20D85EEB000BBEB1 /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = \"<group>\"; };\n\t\t9287EADC20D85EEB000BBEB1 /* cmd_variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_variables.c; sourceTree = \"<group>\"; };\n\t\t9287EADD20D85EEB000BBEB1 /* cmd_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_data.c; sourceTree = \"<group>\"; };\n\t\t9287EADE20D85EEB000BBEB1 /* string_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utils.h; sourceTree = \"<group>\"; };\n\t\t9287EADF20D85EEB000BBEB1 /* rcstring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rcstring.c; sourceTree = \"<group>\"; };\n\t\t9287EAE020D85EEB000BBEB1 /* cmd_subs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_subs.h; sourceTree = \"<group>\"; };\n\t\t9287EAE120D85EEB000BBEB1 /* cmd_background.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_background.c; sourceTree = \"<group>\"; };\n\t\t9287EAE220D85EEB000BBEB1 /* cmd_files.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_files.c; sourceTree = \"<group>\"; };\n\t\t9287EAE320D85EEB000BBEB1 /* cmd_strings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_strings.h; sourceTree = \"<group>\"; };\n\t\t9287EAE420D85EEB000BBEB1 /* cmd_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_control.c; sourceTree = \"<group>\"; };\n\t\t9287EAE520D85EEB000BBEB1 /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = \"<group>\"; };\n\t\t9287EAE620D85EEB000BBEB1 /* variables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = variables.c; sourceTree = \"<group>\"; };\n\t\t9287EAE720D85EEB000BBEB1 /* interpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter.h; sourceTree = \"<group>\"; };\n\t\t9287EAE820D85EEB000BBEB1 /* cmd_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_io.h; sourceTree = \"<group>\"; };\n\t\t9287EAE920D85EEB000BBEB1 /* token.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = token.c; sourceTree = \"<group>\"; };\n\t\t9287EAEA20D85EEB000BBEB1 /* cmd_maths.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_maths.c; sourceTree = \"<group>\"; };\n\t\t9287EAEB20D85EEB000BBEB1 /* tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tokenizer.h; sourceTree = \"<group>\"; };\n\t\t9287EAEC20D85EEB000BBEB1 /* cmd_screen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_screen.h; sourceTree = \"<group>\"; };\n\t\t9287EAED20D85EEB000BBEB1 /* value.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = value.h; sourceTree = \"<group>\"; };\n\t\t9287EAEE20D85EEB000BBEB1 /* charsets.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = charsets.c; sourceTree = \"<group>\"; };\n\t\t9287EAEF20D85EEB000BBEB1 /* cmd_text.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_text.c; sourceTree = \"<group>\"; };\n\t\t9287EAF020D85EEB000BBEB1 /* interpreter_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interpreter_utils.c; sourceTree = \"<group>\"; };\n\t\t9287EAF120D85EEB000BBEB1 /* cmd_sprites.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_sprites.h; sourceTree = \"<group>\"; };\n\t\t9287EAF220D85EEB000BBEB1 /* labels.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = labels.c; sourceTree = \"<group>\"; };\n\t\t9287EAF320D85EEB000BBEB1 /* cmd_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_data.h; sourceTree = \"<group>\"; };\n\t\t9287EAF420D85EEB000BBEB1 /* cmd_variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_variables.h; sourceTree = \"<group>\"; };\n\t\t9287EAF520D85EEB000BBEB1 /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = \"<group>\"; };\n\t\t9287EAF620D85EEB000BBEB1 /* cmd_memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_memory.c; sourceTree = \"<group>\"; };\n\t\t9287EAF720D85EEB000BBEB1 /* cmd_strings.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_strings.c; sourceTree = \"<group>\"; };\n\t\t9287EAF820D85EEB000BBEB1 /* cmd_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_control.h; sourceTree = \"<group>\"; };\n\t\t9287EAF920D85EEB000BBEB1 /* cmd_files.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_files.h; sourceTree = \"<group>\"; };\n\t\t9287EAFA20D85EEB000BBEB1 /* cmd_background.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_background.h; sourceTree = \"<group>\"; };\n\t\t9287EAFB20D85EEB000BBEB1 /* rcstring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rcstring.h; sourceTree = \"<group>\"; };\n\t\t9287EAFC20D85EEB000BBEB1 /* string_utils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = string_utils.c; sourceTree = \"<group>\"; };\n\t\t9287EAFD20D85EEB000BBEB1 /* cmd_subs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_subs.c; sourceTree = \"<group>\"; };\n\t\t9287EAFE20D85EEB000BBEB1 /* token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = token.h; sourceTree = \"<group>\"; };\n\t\t9287EAFF20D85EEB000BBEB1 /* cmd_io.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_io.c; sourceTree = \"<group>\"; };\n\t\t9287EB0020D85EEB000BBEB1 /* interpreter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = interpreter.c; sourceTree = \"<group>\"; };\n\t\t9287EB0120D85EEB000BBEB1 /* variables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = variables.h; sourceTree = \"<group>\"; };\n\t\t9287EB0220D85EEB000BBEB1 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = \"<group>\"; };\n\t\t9287EB0320D85EEB000BBEB1 /* interpreter_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter_config.h; sourceTree = \"<group>\"; };\n\t\t9287EB0420D85EEB000BBEB1 /* cmd_sprites.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_sprites.c; sourceTree = \"<group>\"; };\n\t\t9287EB0520D85EEB000BBEB1 /* labels.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = labels.h; sourceTree = \"<group>\"; };\n\t\t9287EB0620D85EEB000BBEB1 /* charsets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = charsets.h; sourceTree = \"<group>\"; };\n\t\t9287EB0720D85EEB000BBEB1 /* value.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = value.c; sourceTree = \"<group>\"; };\n\t\t9287EB0820D85EEB000BBEB1 /* cmd_screen.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cmd_screen.c; sourceTree = \"<group>\"; };\n\t\t9287EB0920D85EEB000BBEB1 /* interpreter_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = interpreter_utils.h; sourceTree = \"<group>\"; };\n\t\t9287EB0A20D85EEB000BBEB1 /* cmd_text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_text.h; sourceTree = \"<group>\"; };\n\t\t9287EB0B20D85EEB000BBEB1 /* tokenizer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tokenizer.c; sourceTree = \"<group>\"; };\n\t\t9287EB0C20D85EEB000BBEB1 /* cmd_maths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cmd_maths.h; sourceTree = \"<group>\"; };\n\t\t9287EB0D20D85EEB000BBEB1 /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = \"<group>\"; };\n\t\t9287EB0F20D85EEB000BBEB1 /* disk_drive.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = disk_drive.c; sourceTree = \"<group>\"; };\n\t\t9287EB1020D85EEB000BBEB1 /* disk_drive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = disk_drive.h; sourceTree = \"<group>\"; };\n\t\t9287EB1220D85EEB000BBEB1 /* data_manager.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data_manager.c; sourceTree = \"<group>\"; };\n\t\t9287EB1320D85EEB000BBEB1 /* data_manager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data_manager.h; sourceTree = \"<group>\"; };\n\t\t9287EB1520D85EEB000BBEB1 /* sprites_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sprites_lib.c; sourceTree = \"<group>\"; };\n\t\t9287EB1620D85EEB000BBEB1 /* default_characters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = default_characters.h; sourceTree = \"<group>\"; };\n\t\t9287EB1720D85EEB000BBEB1 /* startup_sequence.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = startup_sequence.h; sourceTree = \"<group>\"; };\n\t\t9287EB1820D85EEB000BBEB1 /* text_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = text_lib.h; sourceTree = \"<group>\"; };\n\t\t9287EB1920D85EEB000BBEB1 /* sprites_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sprites_lib.h; sourceTree = \"<group>\"; };\n\t\t9287EB1A20D85EEB000BBEB1 /* default_characters.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = default_characters.c; sourceTree = \"<group>\"; };\n\t\t9287EB1B20D85EEB000BBEB1 /* startup_sequence.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = startup_sequence.c; sourceTree = \"<group>\"; };\n\t\t9287EB1C20D85EEB000BBEB1 /* text_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = text_lib.c; sourceTree = \"<group>\"; };\n\t\t9287EB1D20D85EEB000BBEB1 /* core_delegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core_delegate.h; sourceTree = \"<group>\"; };\n\t\t9287EB1E20D85EEB000BBEB1 /* core.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core.c; sourceTree = \"<group>\"; };\n\t\t9287EB2020D85EEB000BBEB1 /* video_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = video_chip.h; sourceTree = \"<group>\"; };\n\t\t9287EB2120D85EEB000BBEB1 /* machine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = machine.h; sourceTree = \"<group>\"; };\n\t\t9287EB2220D85EEB000BBEB1 /* audio_chip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = audio_chip.c; sourceTree = \"<group>\"; };\n\t\t9287EB2320D85EEB000BBEB1 /* io_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = io_chip.h; sourceTree = \"<group>\"; };\n\t\t9287EB2420D85EEB000BBEB1 /* video_chip.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = video_chip.c; sourceTree = \"<group>\"; };\n\t\t9287EB2520D85EEB000BBEB1 /* machine.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = machine.c; sourceTree = \"<group>\"; };\n\t\t9287EB2620D85EEB000BBEB1 /* audio_chip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audio_chip.h; sourceTree = \"<group>\"; };\n\t\t9287EB2820D85EEB000BBEB1 /* overlay_data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = overlay_data.c; sourceTree = \"<group>\"; };\n\t\t9287EB2920D85EEB000BBEB1 /* overlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = overlay.h; sourceTree = \"<group>\"; };\n\t\t9287EB2A20D85EEB000BBEB1 /* overlay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = overlay.c; sourceTree = \"<group>\"; };\n\t\t9287EB2B20D85EEB000BBEB1 /* overlay_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = overlay_data.h; sourceTree = \"<group>\"; };\n\t\t9287EB2C20D85EEB000BBEB1 /* core_delegate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = core_delegate.c; sourceTree = \"<group>\"; };\n\t\t9287EB5520D85EF7000BBEB1 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = \"<group>\"; };\n\t\t9287EB5820D85F43000BBEB1 /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = ../../../../../../Library/Frameworks/SDL2.framework; sourceTree = \"<group>\"; };\n\t\t928CA2E42189DC370085125F /* runner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = runner.h; sourceTree = \"<group>\"; };\n\t\t928CA2E52189DC370085125F /* runner.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = runner.c; sourceTree = \"<group>\"; };\n\t\t928CA2E72189DCE90085125F /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = \"<group>\"; };\n\t\t92ADAC74218B475C0040A1F1 /* config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = \"<group>\"; };\n\t\t92AF302F219179930053EA80 /* sdl_include.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sdl_include.h; sourceTree = \"<group>\"; };\n\t\t92AF303021917A9D0053EA80 /* stb_image_write.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = stb_image_write.h; sourceTree = \"<group>\"; };\n\t\t92D708922181C0ED00F40043 /* screenshot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = screenshot.h; sourceTree = \"<group>\"; };\n\t\t92D708932181C0ED00F40043 /* screenshot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = screenshot.c; sourceTree = \"<group>\"; };\n\t\t92D708952181C39900F40043 /* libz.1.1.3.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.1.1.3.tbd; path = usr/lib/libz.1.1.3.tbd; sourceTree = SDKROOT; };\n\t\t92D708972181C67000F40043 /* libpng16.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libpng16.a; path = ../../../../../../usr/local/Cellar/libpng/1.6.21/lib/libpng16.a; sourceTree = \"<group>\"; };\n\t\t92D708992181C80200F40043 /* libz.1.2.8.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libz.1.2.8.tbd; path = usr/lib/libz.1.2.8.tbd; sourceTree = SDKROOT; };\n\t\t92D7089C2181E0A400F40043 /* system_paths.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = system_paths.h; sourceTree = \"<group>\"; };\n\t\t92D7089D2181E0A400F40043 /* system_paths.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = system_paths.c; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t9287EABF20D85E4D000BBEB1 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t9287EB5920D85F43000BBEB1 /* SDL2.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t92648F1225BAD356008559AA /* libretro */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t92648F1625BAD73D008559AA /* libretro.h */,\n\t\t\t\t92648F6425BAE851008559AA /* libretro_main.h */,\n\t\t\t\t92648F6525BAE851008559AA /* libretro_main.c */,\n\t\t\t);\n\t\t\tname = libretro;\n\t\t\tpath = ../../libretro;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EAB920D85E4D000BBEB1 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EAD820D85EEB000BBEB1 /* core */,\n\t\t\t\t9287EB5320D85EF7000BBEB1 /* sdl */,\n\t\t\t\t92648F1225BAD356008559AA /* libretro */,\n\t\t\t\t9287EAC420D85E4D000BBEB1 /* LowRes NX macOS */,\n\t\t\t\t9287EAC320D85E4D000BBEB1 /* Products */,\n\t\t\t\t9287EB5720D85F43000BBEB1 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EAC320D85E4D000BBEB1 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EAC220D85E4D000BBEB1 /* LowRes NX.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EAC420D85E4D000BBEB1 /* LowRes NX macOS */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EAC820D85E4E000BBEB1 /* Assets.xcassets */,\n\t\t\t\t9287EACD20D85E4E000BBEB1 /* Info.plist */,\n\t\t\t\t9287EAD020D85E4E000BBEB1 /* LowRes_NX_macOS_SDL.entitlements */,\n\t\t\t);\n\t\t\tpath = \"LowRes NX macOS\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EAD820D85EEB000BBEB1 /* core */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EB2C20D85EEB000BBEB1 /* core_delegate.c */,\n\t\t\t\t9287EB1D20D85EEB000BBEB1 /* core_delegate.h */,\n\t\t\t\t925F6BEC25039D11005E1179 /* core_stats.c */,\n\t\t\t\t925F6BED25039D12005E1179 /* core_stats.h */,\n\t\t\t\t9287EB1E20D85EEB000BBEB1 /* core.c */,\n\t\t\t\t9287EB0D20D85EEB000BBEB1 /* core.h */,\n\t\t\t\t921F986520DAD4DF0052F233 /* boot_intro.c */,\n\t\t\t\t921F986420DAD4DF0052F233 /* boot_intro.h */,\n\t\t\t\t9287EB0E20D85EEB000BBEB1 /* accessories */,\n\t\t\t\t9287EB1120D85EEB000BBEB1 /* datamanager */,\n\t\t\t\t9287EAD920D85EEB000BBEB1 /* interpreter */,\n\t\t\t\t9287EB1420D85EEB000BBEB1 /* libraries */,\n\t\t\t\t9287EB1F20D85EEB000BBEB1 /* machine */,\n\t\t\t\t9287EB2720D85EEB000BBEB1 /* overlay */,\n\t\t\t);\n\t\t\tname = core;\n\t\t\tpath = ../../core;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EAD920D85EEB000BBEB1 /* interpreter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EAEE20D85EEB000BBEB1 /* charsets.c */,\n\t\t\t\t9287EB0620D85EEB000BBEB1 /* charsets.h */,\n\t\t\t\t9251FDE521259EDE003915D9 /* cmd_audio.c */,\n\t\t\t\t9251FDE421259EDE003915D9 /* cmd_audio.h */,\n\t\t\t\t9287EAE120D85EEB000BBEB1 /* cmd_background.c */,\n\t\t\t\t9287EAFA20D85EEB000BBEB1 /* cmd_background.h */,\n\t\t\t\t9287EAE420D85EEB000BBEB1 /* cmd_control.c */,\n\t\t\t\t9287EAF820D85EEB000BBEB1 /* cmd_control.h */,\n\t\t\t\t9287EADD20D85EEB000BBEB1 /* cmd_data.c */,\n\t\t\t\t9287EAF320D85EEB000BBEB1 /* cmd_data.h */,\n\t\t\t\t9287EAE220D85EEB000BBEB1 /* cmd_files.c */,\n\t\t\t\t9287EAF920D85EEB000BBEB1 /* cmd_files.h */,\n\t\t\t\t9287EAFF20D85EEB000BBEB1 /* cmd_io.c */,\n\t\t\t\t9287EAE820D85EEB000BBEB1 /* cmd_io.h */,\n\t\t\t\t9287EAEA20D85EEB000BBEB1 /* cmd_maths.c */,\n\t\t\t\t9287EB0C20D85EEB000BBEB1 /* cmd_maths.h */,\n\t\t\t\t9287EAF620D85EEB000BBEB1 /* cmd_memory.c */,\n\t\t\t\t9287EADA20D85EEB000BBEB1 /* cmd_memory.h */,\n\t\t\t\t9287EB0820D85EEB000BBEB1 /* cmd_screen.c */,\n\t\t\t\t9287EAEC20D85EEB000BBEB1 /* cmd_screen.h */,\n\t\t\t\t9287EB0420D85EEB000BBEB1 /* cmd_sprites.c */,\n\t\t\t\t9287EAF120D85EEB000BBEB1 /* cmd_sprites.h */,\n\t\t\t\t9287EAF720D85EEB000BBEB1 /* cmd_strings.c */,\n\t\t\t\t9287EAE320D85EEB000BBEB1 /* cmd_strings.h */,\n\t\t\t\t9287EAFD20D85EEB000BBEB1 /* cmd_subs.c */,\n\t\t\t\t9287EAE020D85EEB000BBEB1 /* cmd_subs.h */,\n\t\t\t\t9287EAEF20D85EEB000BBEB1 /* cmd_text.c */,\n\t\t\t\t9287EB0A20D85EEB000BBEB1 /* cmd_text.h */,\n\t\t\t\t9287EADC20D85EEB000BBEB1 /* cmd_variables.c */,\n\t\t\t\t9287EAF420D85EEB000BBEB1 /* cmd_variables.h */,\n\t\t\t\t9287EB0220D85EEB000BBEB1 /* data.c */,\n\t\t\t\t9287EAE520D85EEB000BBEB1 /* data.h */,\n\t\t\t\t9287EAF520D85EEB000BBEB1 /* error.c */,\n\t\t\t\t9287EADB20D85EEB000BBEB1 /* error.h */,\n\t\t\t\t9287EB0320D85EEB000BBEB1 /* interpreter_config.h */,\n\t\t\t\t9287EAF020D85EEB000BBEB1 /* interpreter_utils.c */,\n\t\t\t\t9287EB0920D85EEB000BBEB1 /* interpreter_utils.h */,\n\t\t\t\t9287EB0020D85EEB000BBEB1 /* interpreter.c */,\n\t\t\t\t9287EAE720D85EEB000BBEB1 /* interpreter.h */,\n\t\t\t\t9287EAF220D85EEB000BBEB1 /* labels.c */,\n\t\t\t\t9287EB0520D85EEB000BBEB1 /* labels.h */,\n\t\t\t\t9287EADF20D85EEB000BBEB1 /* rcstring.c */,\n\t\t\t\t9287EAFB20D85EEB000BBEB1 /* rcstring.h */,\n\t\t\t\t9287EAFC20D85EEB000BBEB1 /* string_utils.c */,\n\t\t\t\t9287EADE20D85EEB000BBEB1 /* string_utils.h */,\n\t\t\t\t9287EAE920D85EEB000BBEB1 /* token.c */,\n\t\t\t\t9287EAFE20D85EEB000BBEB1 /* token.h */,\n\t\t\t\t9287EB0B20D85EEB000BBEB1 /* tokenizer.c */,\n\t\t\t\t9287EAEB20D85EEB000BBEB1 /* tokenizer.h */,\n\t\t\t\t9287EB0720D85EEB000BBEB1 /* value.c */,\n\t\t\t\t9287EAED20D85EEB000BBEB1 /* value.h */,\n\t\t\t\t9287EAE620D85EEB000BBEB1 /* variables.c */,\n\t\t\t\t9287EB0120D85EEB000BBEB1 /* variables.h */,\n\t\t\t);\n\t\t\tpath = interpreter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EB0E20D85EEB000BBEB1 /* accessories */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EB0F20D85EEB000BBEB1 /* disk_drive.c */,\n\t\t\t\t9287EB1020D85EEB000BBEB1 /* disk_drive.h */,\n\t\t\t);\n\t\t\tpath = accessories;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EB1120D85EEB000BBEB1 /* datamanager */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EB1220D85EEB000BBEB1 /* data_manager.c */,\n\t\t\t\t9287EB1320D85EEB000BBEB1 /* data_manager.h */,\n\t\t\t);\n\t\t\tpath = datamanager;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EB1420D85EEB000BBEB1 /* libraries */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EB1A20D85EEB000BBEB1 /* default_characters.c */,\n\t\t\t\t9287EB1620D85EEB000BBEB1 /* default_characters.h */,\n\t\t\t\t9287EB1B20D85EEB000BBEB1 /* startup_sequence.c */,\n\t\t\t\t9287EB1720D85EEB000BBEB1 /* startup_sequence.h */,\n\t\t\t\t9287EB1520D85EEB000BBEB1 /* sprites_lib.c */,\n\t\t\t\t9287EB1920D85EEB000BBEB1 /* sprites_lib.h */,\n\t\t\t\t9287EB1C20D85EEB000BBEB1 /* text_lib.c */,\n\t\t\t\t9287EB1820D85EEB000BBEB1 /* text_lib.h */,\n\t\t\t\t925D482C2121FF76003F1B0D /* audio_lib.c */,\n\t\t\t\t925D482B2121FF76003F1B0D /* audio_lib.h */,\n\t\t\t);\n\t\t\tpath = libraries;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EB1F20D85EEB000BBEB1 /* machine */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EB2220D85EEB000BBEB1 /* audio_chip.c */,\n\t\t\t\t9287EB2620D85EEB000BBEB1 /* audio_chip.h */,\n\t\t\t\t9287EB2320D85EEB000BBEB1 /* io_chip.h */,\n\t\t\t\t9287EB2520D85EEB000BBEB1 /* machine.c */,\n\t\t\t\t9287EB2120D85EEB000BBEB1 /* machine.h */,\n\t\t\t\t9287EB2420D85EEB000BBEB1 /* video_chip.c */,\n\t\t\t\t9287EB2020D85EEB000BBEB1 /* video_chip.h */,\n\t\t\t);\n\t\t\tpath = machine;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EB2720D85EEB000BBEB1 /* overlay */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9287EB2820D85EEB000BBEB1 /* overlay_data.c */,\n\t\t\t\t9287EB2B20D85EEB000BBEB1 /* overlay_data.h */,\n\t\t\t\t9287EB2A20D85EEB000BBEB1 /* overlay.c */,\n\t\t\t\t9287EB2920D85EEB000BBEB1 /* overlay.h */,\n\t\t\t);\n\t\t\tpath = overlay;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EB5320D85EF7000BBEB1 /* sdl */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t92ADAC74218B475C0040A1F1 /* config.h */,\n\t\t\t\t92AF302F219179930053EA80 /* sdl_include.h */,\n\t\t\t\t928CA2E72189DCE90085125F /* main.h */,\n\t\t\t\t9287EB5520D85EF7000BBEB1 /* main.c */,\n\t\t\t\t928CA2E42189DC370085125F /* runner.h */,\n\t\t\t\t928CA2E52189DC370085125F /* runner.c */,\n\t\t\t\t9284722920F9F4FA00B82653 /* dev_menu.h */,\n\t\t\t\t9284722A20F9F4FA00B82653 /* dev_menu.c */,\n\t\t\t\t9284722C20F9FA5900B82653 /* dev_menu_data.h */,\n\t\t\t\t921E0F132104BD7100F3C512 /* settings.h */,\n\t\t\t\t921E0F142104BD7100F3C512 /* settings.c */,\n\t\t\t\t92D708922181C0ED00F40043 /* screenshot.h */,\n\t\t\t\t92D708932181C0ED00F40043 /* screenshot.c */,\n\t\t\t\t92AF303021917A9D0053EA80 /* stb_image_write.h */,\n\t\t\t\t92D7089C2181E0A400F40043 /* system_paths.h */,\n\t\t\t\t92D7089D2181E0A400F40043 /* system_paths.c */,\n\t\t\t\t9258F205218C49C000CE81D6 /* utils.h */,\n\t\t\t\t9258F206218C49C000CE81D6 /* utils.c */,\n\t\t\t);\n\t\t\tname = sdl;\n\t\t\tpath = ../../sdl;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9287EB5720D85F43000BBEB1 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t92D708992181C80200F40043 /* libz.1.2.8.tbd */,\n\t\t\t\t92D708972181C67000F40043 /* libpng16.a */,\n\t\t\t\t92D708952181C39900F40043 /* libz.1.1.3.tbd */,\n\t\t\t\t9287EB5820D85F43000BBEB1 /* SDL2.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t9287EAC120D85E4D000BBEB1 /* LowRes NX */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 9287EAD320D85E4E000BBEB1 /* Build configuration list for PBXNativeTarget \"LowRes NX\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t9287EABE20D85E4D000BBEB1 /* Sources */,\n\t\t\t\t9287EABF20D85E4D000BBEB1 /* Frameworks */,\n\t\t\t\t9287EAC020D85E4D000BBEB1 /* Resources */,\n\t\t\t\t9289C402212866E8009BE093 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"LowRes NX\";\n\t\t\tproductName = \"LowRes NX macOS SDL\";\n\t\t\tproductReference = 9287EAC220D85E4D000BBEB1 /* LowRes NX.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t9287EABA20D85E4D000BBEB1 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1130;\n\t\t\t\tORGANIZATIONNAME = \"Inutilis Software\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t9287EAC120D85E4D000BBEB1 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.3;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 0;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 9287EABD20D85E4D000BBEB1 /* Build configuration list for PBXProject \"LowRes NX macOS\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 9287EAB920D85E4D000BBEB1;\n\t\t\tproductRefGroup = 9287EAC320D85E4D000BBEB1 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t9287EAC120D85E4D000BBEB1 /* LowRes NX */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t9287EAC020D85E4D000BBEB1 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t9287EAC920D85E4E000BBEB1 /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t9287EABE20D85E4D000BBEB1 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t9287EB3420D85EEB000BBEB1 /* token.c in Sources */,\n\t\t\t\t9287EB3220D85EEB000BBEB1 /* cmd_control.c in Sources */,\n\t\t\t\t9287EB2D20D85EEB000BBEB1 /* cmd_variables.c in Sources */,\n\t\t\t\t928CA2E62189DC370085125F /* runner.c in Sources */,\n\t\t\t\t9287EB4020D85EEB000BBEB1 /* interpreter.c in Sources */,\n\t\t\t\t9287EB5020D85EEB000BBEB1 /* overlay_data.c in Sources */,\n\t\t\t\t9287EB4220D85EEB000BBEB1 /* cmd_sprites.c in Sources */,\n\t\t\t\t9287EB4120D85EEB000BBEB1 /* data.c in Sources */,\n\t\t\t\t9287EB2E20D85EEB000BBEB1 /* cmd_data.c in Sources */,\n\t\t\t\t9258F207218C49C000CE81D6 /* utils.c in Sources */,\n\t\t\t\t9287EB3E20D85EEB000BBEB1 /* cmd_subs.c in Sources */,\n\t\t\t\t9284722B20F9F4FA00B82653 /* dev_menu.c in Sources */,\n\t\t\t\t9287EB4520D85EEB000BBEB1 /* tokenizer.c in Sources */,\n\t\t\t\t9287EB4D20D85EEB000BBEB1 /* audio_chip.c in Sources */,\n\t\t\t\t9287EB5220D85EEB000BBEB1 /* core_delegate.c in Sources */,\n\t\t\t\t9287EB4A20D85EEB000BBEB1 /* startup_sequence.c in Sources */,\n\t\t\t\t921E0F152104BD7100F3C512 /* settings.c in Sources */,\n\t\t\t\t9287EB4420D85EEB000BBEB1 /* cmd_screen.c in Sources */,\n\t\t\t\t9287EB4720D85EEB000BBEB1 /* data_manager.c in Sources */,\n\t\t\t\t92D708942181C0ED00F40043 /* screenshot.c in Sources */,\n\t\t\t\t9287EB2F20D85EEB000BBEB1 /* rcstring.c in Sources */,\n\t\t\t\t9287EB5120D85EEB000BBEB1 /* overlay.c in Sources */,\n\t\t\t\t921F986620DAD4DF0052F233 /* boot_intro.c in Sources */,\n\t\t\t\t92D7089E2181E0A400F40043 /* system_paths.c in Sources */,\n\t\t\t\t9287EB3620D85EEB000BBEB1 /* charsets.c in Sources */,\n\t\t\t\t9251FDE621259EDE003915D9 /* cmd_audio.c in Sources */,\n\t\t\t\t9287EB3F20D85EEB000BBEB1 /* cmd_io.c in Sources */,\n\t\t\t\t9287EB3520D85EEB000BBEB1 /* cmd_maths.c in Sources */,\n\t\t\t\t9287EB4C20D85EEB000BBEB1 /* core.c in Sources */,\n\t\t\t\t9287EB3320D85EEB000BBEB1 /* variables.c in Sources */,\n\t\t\t\t9287EB3C20D85EEB000BBEB1 /* cmd_strings.c in Sources */,\n\t\t\t\t9287EB3B20D85EEB000BBEB1 /* cmd_memory.c in Sources */,\n\t\t\t\t9287EB4920D85EEB000BBEB1 /* default_characters.c in Sources */,\n\t\t\t\t9287EB5620D85EF7000BBEB1 /* main.c in Sources */,\n\t\t\t\t9287EB4E20D85EEB000BBEB1 /* video_chip.c in Sources */,\n\t\t\t\t9287EB3A20D85EEB000BBEB1 /* error.c in Sources */,\n\t\t\t\t9287EB4B20D85EEB000BBEB1 /* text_lib.c in Sources */,\n\t\t\t\t9287EB4620D85EEB000BBEB1 /* disk_drive.c in Sources */,\n\t\t\t\t9287EB4F20D85EEB000BBEB1 /* machine.c in Sources */,\n\t\t\t\t9287EB3120D85EEB000BBEB1 /* cmd_files.c in Sources */,\n\t\t\t\t9287EB3920D85EEB000BBEB1 /* labels.c in Sources */,\n\t\t\t\t9287EB3D20D85EEB000BBEB1 /* string_utils.c in Sources */,\n\t\t\t\t9287EB3820D85EEB000BBEB1 /* interpreter_utils.c in Sources */,\n\t\t\t\t9287EB4320D85EEB000BBEB1 /* value.c in Sources */,\n\t\t\t\t9287EB4820D85EEB000BBEB1 /* sprites_lib.c in Sources */,\n\t\t\t\t925D482D2121FF76003F1B0D /* audio_lib.c in Sources */,\n\t\t\t\t925F6BEE25039D12005E1179 /* core_stats.c in Sources */,\n\t\t\t\t9287EB3020D85EEB000BBEB1 /* cmd_background.c in Sources */,\n\t\t\t\t9287EB3720D85EEB000BBEB1 /* cmd_text.c in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\t9287EAD120D85E4E000BBEB1 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Mac Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.6;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t9287EAD220D85E4E000BBEB1 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Mac Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.6;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t9287EAD420D85E4E000BBEB1 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Mac Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 24;\n\t\t\t\tDEVELOPMENT_TEAM = BZ4VC623NH;\n\t\t\t\tENABLE_HARDENED_RUNTIME = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(LOCAL_LIBRARY_DIR)/Frameworks\",\n\t\t\t\t);\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tINFOPLIST_FILE = \"LowRes NX macOS/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/usr/local/lib,\n\t\t\t\t\t/usr/local/Cellar/libpng/1.6.21/lib,\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.2;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.inutilis.LowRes-NX-macOS\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t9287EAD520D85E4E000BBEB1 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCODE_SIGN_IDENTITY = \"Mac Developer\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 24;\n\t\t\t\tDEVELOPMENT_TEAM = BZ4VC623NH;\n\t\t\t\tENABLE_HARDENED_RUNTIME = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(LOCAL_LIBRARY_DIR)/Frameworks\",\n\t\t\t\t);\n\t\t\t\tHEADER_SEARCH_PATHS = /usr/local/include;\n\t\t\t\tINFOPLIST_FILE = \"LowRes NX macOS/Info.plist\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t/usr/local/lib,\n\t\t\t\t\t/usr/local/Cellar/libpng/1.6.21/lib,\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.2;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"com.inutilis.LowRes-NX-macOS\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t9287EABD20D85E4D000BBEB1 /* Build configuration list for PBXProject \"LowRes NX macOS\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t9287EAD120D85E4E000BBEB1 /* Debug */,\n\t\t\t\t9287EAD220D85E4E000BBEB1 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t9287EAD320D85E4E000BBEB1 /* Build configuration list for PBXNativeTarget \"LowRes NX\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t9287EAD420D85E4E000BBEB1 /* Debug */,\n\t\t\t\t9287EAD520D85E4E000BBEB1 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 9287EABA20D85E4D000BBEB1 /* Project object */;\n}\n"
  },
  {
    "path": "platform/macOS/LowRes NX macOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:/Users/timokloss/projects/lowres-nx/platform/macOS/LowRes NX macOS.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "platform/macOS/LowRes NX macOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "platform/web/README.md",
    "content": "These lines are interesting, but still not a real guide for compiling:\n\nsource ./emsdk_env.sh\nemmake make\n\nAlways clean before build, otherwise changed files will be ignored!\nemmake make clean"
  },
  {
    "path": "platform/web/embed/package/LowResNX120.js",
    "content": "\n\n// The Module object: Our interface to the outside world. We import\n// and export values on it. There are various ways Module can be used:\n// 1. Not defined. We create it here\n// 2. A function parameter, function(Module) { ..generated code.. }\n// 3. pre-run appended it, var Module = {}; ..generated code..\n// 4. External script tag defines var Module.\n// We need to check if Module already exists (e.g. case 3 above).\n// Substitution will be replaced with actual code on later stage of the build,\n// this way Closure Compiler will not mangle it (e.g. case 4. above).\n// Note that if you want to run closure, and also to use Module\n// after the generated code, you will need to define   var Module = {};\n// before the code. Then that object will be used in the code, and you\n// can continue to use Module afterwards as well.\nvar Module = typeof Module !== 'undefined' ? Module : {};\n\n// --pre-jses are emitted after the Module integration code, so that they can\n// refer to Module (if they choose; they can also define Module)\n// {{PRE_JSES}}\n\n// Sometimes an existing Module object exists with properties\n// meant to overwrite the default module functionality. Here\n// we collect those properties and reapply _after_ we configure\n// the current environment's defaults to avoid having to be so\n// defensive during initialization.\nvar moduleOverrides = {};\nvar key;\nfor (key in Module) {\n  if (Module.hasOwnProperty(key)) {\n    moduleOverrides[key] = Module[key];\n  }\n}\n\nvar arguments_ = [];\nvar thisProgram = './this.program';\nvar quit_ = function(status, toThrow) {\n  throw toThrow;\n};\n\n// Determine the runtime environment we are in. You can customize this by\n// setting the ENVIRONMENT setting at compile time (see settings.js).\n\nvar ENVIRONMENT_IS_WEB = false;\nvar ENVIRONMENT_IS_WORKER = false;\nvar ENVIRONMENT_IS_NODE = false;\nvar ENVIRONMENT_IS_SHELL = false;\nENVIRONMENT_IS_WEB = typeof window === 'object';\nENVIRONMENT_IS_WORKER = typeof importScripts === 'function';\n// N.b. Electron.js environment is simultaneously a NODE-environment, but\n// also a web environment.\nENVIRONMENT_IS_NODE = typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node === 'string';\nENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;\n\nif (Module['ENVIRONMENT']) {\n  throw new Error('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -s ENVIRONMENT=web or -s ENVIRONMENT=node)');\n}\n\n// `/` should be present at the end if `scriptDirectory` is not empty\nvar scriptDirectory = '';\nfunction locateFile(path) {\n  if (Module['locateFile']) {\n    return Module['locateFile'](path, scriptDirectory);\n  }\n  return scriptDirectory + path;\n}\n\n// Hooks that are implemented differently in different runtime environments.\nvar read_,\n    readAsync,\n    readBinary,\n    setWindowTitle;\n\nvar nodeFS;\nvar nodePath;\n\nif (ENVIRONMENT_IS_NODE) {\n  if (ENVIRONMENT_IS_WORKER) {\n    scriptDirectory = require('path').dirname(scriptDirectory) + '/';\n  } else {\n    scriptDirectory = __dirname + '/';\n  }\n\n// include: node_shell_read.js\n\n\nread_ = function shell_read(filename, binary) {\n  if (!nodeFS) nodeFS = require('fs');\n  if (!nodePath) nodePath = require('path');\n  filename = nodePath['normalize'](filename);\n  return nodeFS['readFileSync'](filename, binary ? null : 'utf8');\n};\n\nreadBinary = function readBinary(filename) {\n  var ret = read_(filename, true);\n  if (!ret.buffer) {\n    ret = new Uint8Array(ret);\n  }\n  assert(ret.buffer);\n  return ret;\n};\n\n// end include: node_shell_read.js\n  if (process['argv'].length > 1) {\n    thisProgram = process['argv'][1].replace(/\\\\/g, '/');\n  }\n\n  arguments_ = process['argv'].slice(2);\n\n  if (typeof module !== 'undefined') {\n    module['exports'] = Module;\n  }\n\n  process['on']('uncaughtException', function(ex) {\n    // suppress ExitStatus exceptions from showing an error\n    if (!(ex instanceof ExitStatus)) {\n      throw ex;\n    }\n  });\n\n  process['on']('unhandledRejection', abort);\n\n  quit_ = function(status) {\n    process['exit'](status);\n  };\n\n  Module['inspect'] = function () { return '[Emscripten Module object]'; };\n\n} else\nif (ENVIRONMENT_IS_SHELL) {\n\n  if (typeof read != 'undefined') {\n    read_ = function shell_read(f) {\n      return read(f);\n    };\n  }\n\n  readBinary = function readBinary(f) {\n    var data;\n    if (typeof readbuffer === 'function') {\n      return new Uint8Array(readbuffer(f));\n    }\n    data = read(f, 'binary');\n    assert(typeof data === 'object');\n    return data;\n  };\n\n  if (typeof scriptArgs != 'undefined') {\n    arguments_ = scriptArgs;\n  } else if (typeof arguments != 'undefined') {\n    arguments_ = arguments;\n  }\n\n  if (typeof quit === 'function') {\n    quit_ = function(status) {\n      quit(status);\n    };\n  }\n\n  if (typeof print !== 'undefined') {\n    // Prefer to use print/printErr where they exist, as they usually work better.\n    if (typeof console === 'undefined') console = /** @type{!Console} */({});\n    console.log = /** @type{!function(this:Console, ...*): undefined} */ (print);\n    console.warn = console.error = /** @type{!function(this:Console, ...*): undefined} */ (typeof printErr !== 'undefined' ? printErr : print);\n  }\n\n} else\n\n// Note that this includes Node.js workers when relevant (pthreads is enabled).\n// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and\n// ENVIRONMENT_IS_NODE.\nif (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {\n  if (ENVIRONMENT_IS_WORKER) { // Check worker, not web, since window could be polyfilled\n    scriptDirectory = self.location.href;\n  } else if (typeof document !== 'undefined' && document.currentScript) { // web\n    scriptDirectory = document.currentScript.src;\n  }\n  // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them.\n  // otherwise, slice off the final part of the url to find the script directory.\n  // if scriptDirectory does not contain a slash, lastIndexOf will return -1,\n  // and scriptDirectory will correctly be replaced with an empty string.\n  if (scriptDirectory.indexOf('blob:') !== 0) {\n    scriptDirectory = scriptDirectory.substr(0, scriptDirectory.lastIndexOf('/')+1);\n  } else {\n    scriptDirectory = '';\n  }\n\n  // Differentiate the Web Worker from the Node Worker case, as reading must\n  // be done differently.\n  {\n\n// include: web_or_worker_shell_read.js\n\n\n  read_ = function(url) {\n      var xhr = new XMLHttpRequest();\n      xhr.open('GET', url, false);\n      xhr.send(null);\n      return xhr.responseText;\n  };\n\n  if (ENVIRONMENT_IS_WORKER) {\n    readBinary = function(url) {\n        var xhr = new XMLHttpRequest();\n        xhr.open('GET', url, false);\n        xhr.responseType = 'arraybuffer';\n        xhr.send(null);\n        return new Uint8Array(/** @type{!ArrayBuffer} */(xhr.response));\n    };\n  }\n\n  readAsync = function(url, onload, onerror) {\n    var xhr = new XMLHttpRequest();\n    xhr.open('GET', url, true);\n    xhr.responseType = 'arraybuffer';\n    xhr.onload = function() {\n      if (xhr.status == 200 || (xhr.status == 0 && xhr.response)) { // file URLs can return 0\n        onload(xhr.response);\n        return;\n      }\n      onerror();\n    };\n    xhr.onerror = onerror;\n    xhr.send(null);\n  };\n\n// end include: web_or_worker_shell_read.js\n  }\n\n  setWindowTitle = function(title) { document.title = title };\n} else\n{\n  throw new Error('environment detection error');\n}\n\n// Set up the out() and err() hooks, which are how we can print to stdout or\n// stderr, respectively.\nvar out = Module['print'] || console.log.bind(console);\nvar err = Module['printErr'] || console.warn.bind(console);\n\n// Merge back in the overrides\nfor (key in moduleOverrides) {\n  if (moduleOverrides.hasOwnProperty(key)) {\n    Module[key] = moduleOverrides[key];\n  }\n}\n// Free the object hierarchy contained in the overrides, this lets the GC\n// reclaim data used e.g. in memoryInitializerRequest, which is a large typed array.\nmoduleOverrides = null;\n\n// Emit code to handle expected values on the Module object. This applies Module.x\n// to the proper local x. This has two benefits: first, we only emit it if it is\n// expected to arrive, and second, by using a local everywhere else that can be\n// minified.\n\nif (Module['arguments']) arguments_ = Module['arguments'];\nif (!Object.getOwnPropertyDescriptor(Module, 'arguments')) {\n  Object.defineProperty(Module, 'arguments', {\n    configurable: true,\n    get: function() {\n      abort('Module.arguments has been replaced with plain arguments_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\n\nif (Module['thisProgram']) thisProgram = Module['thisProgram'];\nif (!Object.getOwnPropertyDescriptor(Module, 'thisProgram')) {\n  Object.defineProperty(Module, 'thisProgram', {\n    configurable: true,\n    get: function() {\n      abort('Module.thisProgram has been replaced with plain thisProgram (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\n\nif (Module['quit']) quit_ = Module['quit'];\nif (!Object.getOwnPropertyDescriptor(Module, 'quit')) {\n  Object.defineProperty(Module, 'quit', {\n    configurable: true,\n    get: function() {\n      abort('Module.quit has been replaced with plain quit_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\n\n// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message\n// Assertions on removed incoming Module JS APIs.\nassert(typeof Module['memoryInitializerPrefixURL'] === 'undefined', 'Module.memoryInitializerPrefixURL option was removed, use Module.locateFile instead');\nassert(typeof Module['pthreadMainPrefixURL'] === 'undefined', 'Module.pthreadMainPrefixURL option was removed, use Module.locateFile instead');\nassert(typeof Module['cdInitializerPrefixURL'] === 'undefined', 'Module.cdInitializerPrefixURL option was removed, use Module.locateFile instead');\nassert(typeof Module['filePackagePrefixURL'] === 'undefined', 'Module.filePackagePrefixURL option was removed, use Module.locateFile instead');\nassert(typeof Module['read'] === 'undefined', 'Module.read option was removed (modify read_ in JS)');\nassert(typeof Module['readAsync'] === 'undefined', 'Module.readAsync option was removed (modify readAsync in JS)');\nassert(typeof Module['readBinary'] === 'undefined', 'Module.readBinary option was removed (modify readBinary in JS)');\nassert(typeof Module['setWindowTitle'] === 'undefined', 'Module.setWindowTitle option was removed (modify setWindowTitle in JS)');\nassert(typeof Module['TOTAL_MEMORY'] === 'undefined', 'Module.TOTAL_MEMORY has been renamed Module.INITIAL_MEMORY');\n\nif (!Object.getOwnPropertyDescriptor(Module, 'read')) {\n  Object.defineProperty(Module, 'read', {\n    configurable: true,\n    get: function() {\n      abort('Module.read has been replaced with plain read_ (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\n\nif (!Object.getOwnPropertyDescriptor(Module, 'readAsync')) {\n  Object.defineProperty(Module, 'readAsync', {\n    configurable: true,\n    get: function() {\n      abort('Module.readAsync has been replaced with plain readAsync (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\n\nif (!Object.getOwnPropertyDescriptor(Module, 'readBinary')) {\n  Object.defineProperty(Module, 'readBinary', {\n    configurable: true,\n    get: function() {\n      abort('Module.readBinary has been replaced with plain readBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\n\nif (!Object.getOwnPropertyDescriptor(Module, 'setWindowTitle')) {\n  Object.defineProperty(Module, 'setWindowTitle', {\n    configurable: true,\n    get: function() {\n      abort('Module.setWindowTitle has been replaced with plain setWindowTitle (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\nvar IDBFS = 'IDBFS is no longer included by default; build with -lidbfs.js';\nvar PROXYFS = 'PROXYFS is no longer included by default; build with -lproxyfs.js';\nvar WORKERFS = 'WORKERFS is no longer included by default; build with -lworkerfs.js';\nvar NODEFS = 'NODEFS is no longer included by default; build with -lnodefs.js';\n\n\n\n\nvar STACK_ALIGN = 16;\n\nfunction alignMemory(size, factor) {\n  if (!factor) factor = STACK_ALIGN; // stack alignment (16-byte) by default\n  return Math.ceil(size / factor) * factor;\n}\n\nfunction getNativeTypeSize(type) {\n  switch (type) {\n    case 'i1': case 'i8': return 1;\n    case 'i16': return 2;\n    case 'i32': return 4;\n    case 'i64': return 8;\n    case 'float': return 4;\n    case 'double': return 8;\n    default: {\n      if (type[type.length-1] === '*') {\n        return 4; // A pointer\n      } else if (type[0] === 'i') {\n        var bits = Number(type.substr(1));\n        assert(bits % 8 === 0, 'getNativeTypeSize invalid bits ' + bits + ', type ' + type);\n        return bits / 8;\n      } else {\n        return 0;\n      }\n    }\n  }\n}\n\nfunction warnOnce(text) {\n  if (!warnOnce.shown) warnOnce.shown = {};\n  if (!warnOnce.shown[text]) {\n    warnOnce.shown[text] = 1;\n    err(text);\n  }\n}\n\n// include: runtime_functions.js\n\n\n// Wraps a JS function as a wasm function with a given signature.\nfunction convertJsFunctionToWasm(func, sig) {\n\n  // If the type reflection proposal is available, use the new\n  // \"WebAssembly.Function\" constructor.\n  // Otherwise, construct a minimal wasm module importing the JS function and\n  // re-exporting it.\n  if (typeof WebAssembly.Function === \"function\") {\n    var typeNames = {\n      'i': 'i32',\n      'j': 'i64',\n      'f': 'f32',\n      'd': 'f64'\n    };\n    var type = {\n      parameters: [],\n      results: sig[0] == 'v' ? [] : [typeNames[sig[0]]]\n    };\n    for (var i = 1; i < sig.length; ++i) {\n      type.parameters.push(typeNames[sig[i]]);\n    }\n    return new WebAssembly.Function(type, func);\n  }\n\n  // The module is static, with the exception of the type section, which is\n  // generated based on the signature passed in.\n  var typeSection = [\n    0x01, // id: section,\n    0x00, // length: 0 (placeholder)\n    0x01, // count: 1\n    0x60, // form: func\n  ];\n  var sigRet = sig.slice(0, 1);\n  var sigParam = sig.slice(1);\n  var typeCodes = {\n    'i': 0x7f, // i32\n    'j': 0x7e, // i64\n    'f': 0x7d, // f32\n    'd': 0x7c, // f64\n  };\n\n  // Parameters, length + signatures\n  typeSection.push(sigParam.length);\n  for (var i = 0; i < sigParam.length; ++i) {\n    typeSection.push(typeCodes[sigParam[i]]);\n  }\n\n  // Return values, length + signatures\n  // With no multi-return in MVP, either 0 (void) or 1 (anything else)\n  if (sigRet == 'v') {\n    typeSection.push(0x00);\n  } else {\n    typeSection = typeSection.concat([0x01, typeCodes[sigRet]]);\n  }\n\n  // Write the overall length of the type section back into the section header\n  // (excepting the 2 bytes for the section id and length)\n  typeSection[1] = typeSection.length - 2;\n\n  // Rest of the module is static\n  var bytes = new Uint8Array([\n    0x00, 0x61, 0x73, 0x6d, // magic (\"\\0asm\")\n    0x01, 0x00, 0x00, 0x00, // version: 1\n  ].concat(typeSection, [\n    0x02, 0x07, // import section\n      // (import \"e\" \"f\" (func 0 (type 0)))\n      0x01, 0x01, 0x65, 0x01, 0x66, 0x00, 0x00,\n    0x07, 0x05, // export section\n      // (export \"f\" (func 0 (type 0)))\n      0x01, 0x01, 0x66, 0x00, 0x00,\n  ]));\n\n   // We can compile this wasm module synchronously because it is very small.\n  // This accepts an import (at \"e.f\"), that it reroutes to an export (at \"f\")\n  var module = new WebAssembly.Module(bytes);\n  var instance = new WebAssembly.Instance(module, {\n    'e': {\n      'f': func\n    }\n  });\n  var wrappedFunc = instance.exports['f'];\n  return wrappedFunc;\n}\n\nvar freeTableIndexes = [];\n\n// Weak map of functions in the table to their indexes, created on first use.\nvar functionsInTableMap;\n\nfunction getEmptyTableSlot() {\n  // Reuse a free index if there is one, otherwise grow.\n  if (freeTableIndexes.length) {\n    return freeTableIndexes.pop();\n  }\n  // Grow the table\n  try {\n    wasmTable.grow(1);\n  } catch (err) {\n    if (!(err instanceof RangeError)) {\n      throw err;\n    }\n    throw 'Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.';\n  }\n  return wasmTable.length - 1;\n}\n\n// Add a wasm function to the table.\nfunction addFunctionWasm(func, sig) {\n  // Check if the function is already in the table, to ensure each function\n  // gets a unique index. First, create the map if this is the first use.\n  if (!functionsInTableMap) {\n    functionsInTableMap = new WeakMap();\n    for (var i = 0; i < wasmTable.length; i++) {\n      var item = wasmTable.get(i);\n      // Ignore null values.\n      if (item) {\n        functionsInTableMap.set(item, i);\n      }\n    }\n  }\n  if (functionsInTableMap.has(func)) {\n    return functionsInTableMap.get(func);\n  }\n\n  // It's not in the table, add it now.\n\n  var ret = getEmptyTableSlot();\n\n  // Set the new value.\n  try {\n    // Attempting to call this with JS function will cause of table.set() to fail\n    wasmTable.set(ret, func);\n  } catch (err) {\n    if (!(err instanceof TypeError)) {\n      throw err;\n    }\n    assert(typeof sig !== 'undefined', 'Missing signature argument to addFunction: ' + func);\n    var wrapped = convertJsFunctionToWasm(func, sig);\n    wasmTable.set(ret, wrapped);\n  }\n\n  functionsInTableMap.set(func, ret);\n\n  return ret;\n}\n\nfunction removeFunction(index) {\n  functionsInTableMap.delete(wasmTable.get(index));\n  freeTableIndexes.push(index);\n}\n\n// 'sig' parameter is required for the llvm backend but only when func is not\n// already a WebAssembly function.\nfunction addFunction(func, sig) {\n  assert(typeof func !== 'undefined');\n\n  return addFunctionWasm(func, sig);\n}\n\n// end include: runtime_functions.js\n// include: runtime_debug.js\n\n\n// end include: runtime_debug.js\nfunction makeBigInt(low, high, unsigned) {\n  return unsigned ? ((+((low>>>0)))+((+((high>>>0)))*4294967296.0)) : ((+((low>>>0)))+((+((high|0)))*4294967296.0));\n}\n\nvar tempRet0 = 0;\n\nvar setTempRet0 = function(value) {\n  tempRet0 = value;\n};\n\nvar getTempRet0 = function() {\n  return tempRet0;\n};\n\nfunction getCompilerSetting(name) {\n  throw 'You must build with -s RETAIN_COMPILER_SETTINGS=1 for getCompilerSetting or emscripten_get_compiler_setting to work';\n}\n\n\n\n// === Preamble library stuff ===\n\n// Documentation for the public APIs defined in this file must be updated in:\n//    site/source/docs/api_reference/preamble.js.rst\n// A prebuilt local version of the documentation is available at:\n//    site/build/text/docs/api_reference/preamble.js.txt\n// You can also build docs locally as HTML or other formats in site/\n// An online HTML version (which may be of a different version of Emscripten)\n//    is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html\n\nvar wasmBinary;\nif (Module['wasmBinary']) wasmBinary = Module['wasmBinary'];\nif (!Object.getOwnPropertyDescriptor(Module, 'wasmBinary')) {\n  Object.defineProperty(Module, 'wasmBinary', {\n    configurable: true,\n    get: function() {\n      abort('Module.wasmBinary has been replaced with plain wasmBinary (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\nvar noExitRuntime = Module['noExitRuntime'] || true;\nif (!Object.getOwnPropertyDescriptor(Module, 'noExitRuntime')) {\n  Object.defineProperty(Module, 'noExitRuntime', {\n    configurable: true,\n    get: function() {\n      abort('Module.noExitRuntime has been replaced with plain noExitRuntime (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\n\nif (typeof WebAssembly !== 'object') {\n  abort('no native wasm support detected');\n}\n\n// include: runtime_safe_heap.js\n\n\n// In MINIMAL_RUNTIME, setValue() and getValue() are only available when building with safe heap enabled, for heap safety checking.\n// In traditional runtime, setValue() and getValue() are always available (although their use is highly discouraged due to perf penalties)\n\n/** @param {number} ptr\n    @param {number} value\n    @param {string} type\n    @param {number|boolean=} noSafe */\nfunction setValue(ptr, value, type, noSafe) {\n  type = type || 'i8';\n  if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit\n    switch(type) {\n      case 'i1': HEAP8[((ptr)>>0)] = value; break;\n      case 'i8': HEAP8[((ptr)>>0)] = value; break;\n      case 'i16': HEAP16[((ptr)>>1)] = value; break;\n      case 'i32': HEAP32[((ptr)>>2)] = value; break;\n      case 'i64': (tempI64 = [value>>>0,(tempDouble=value,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((ptr)>>2)] = tempI64[0],HEAP32[(((ptr)+(4))>>2)] = tempI64[1]); break;\n      case 'float': HEAPF32[((ptr)>>2)] = value; break;\n      case 'double': HEAPF64[((ptr)>>3)] = value; break;\n      default: abort('invalid type for setValue: ' + type);\n    }\n}\n\n/** @param {number} ptr\n    @param {string} type\n    @param {number|boolean=} noSafe */\nfunction getValue(ptr, type, noSafe) {\n  type = type || 'i8';\n  if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit\n    switch(type) {\n      case 'i1': return HEAP8[((ptr)>>0)];\n      case 'i8': return HEAP8[((ptr)>>0)];\n      case 'i16': return HEAP16[((ptr)>>1)];\n      case 'i32': return HEAP32[((ptr)>>2)];\n      case 'i64': return HEAP32[((ptr)>>2)];\n      case 'float': return HEAPF32[((ptr)>>2)];\n      case 'double': return HEAPF64[((ptr)>>3)];\n      default: abort('invalid type for getValue: ' + type);\n    }\n  return null;\n}\n\n// end include: runtime_safe_heap.js\n// Wasm globals\n\nvar wasmMemory;\n\n//========================================\n// Runtime essentials\n//========================================\n\n// whether we are quitting the application. no code should run after this.\n// set in exit() and abort()\nvar ABORT = false;\n\n// set by exit() and abort().  Passed to 'onExit' handler.\n// NOTE: This is also used as the process return code code in shell environments\n// but only when noExitRuntime is false.\nvar EXITSTATUS;\n\n/** @type {function(*, string=)} */\nfunction assert(condition, text) {\n  if (!condition) {\n    abort('Assertion failed: ' + text);\n  }\n}\n\n// Returns the C function with a specified identifier (for C++, you need to do manual name mangling)\nfunction getCFunc(ident) {\n  var func = Module['_' + ident]; // closure exported function\n  assert(func, 'Cannot call unknown function ' + ident + ', make sure it is exported');\n  return func;\n}\n\n// C calling interface.\n/** @param {string|null=} returnType\n    @param {Array=} argTypes\n    @param {Arguments|Array=} args\n    @param {Object=} opts */\nfunction ccall(ident, returnType, argTypes, args, opts) {\n  // For fast lookup of conversion functions\n  var toC = {\n    'string': function(str) {\n      var ret = 0;\n      if (str !== null && str !== undefined && str !== 0) { // null string\n        // at most 4 bytes per UTF-8 code point, +1 for the trailing '\\0'\n        var len = (str.length << 2) + 1;\n        ret = stackAlloc(len);\n        stringToUTF8(str, ret, len);\n      }\n      return ret;\n    },\n    'array': function(arr) {\n      var ret = stackAlloc(arr.length);\n      writeArrayToMemory(arr, ret);\n      return ret;\n    }\n  };\n\n  function convertReturnValue(ret) {\n    if (returnType === 'string') return UTF8ToString(ret);\n    if (returnType === 'boolean') return Boolean(ret);\n    return ret;\n  }\n\n  var func = getCFunc(ident);\n  var cArgs = [];\n  var stack = 0;\n  assert(returnType !== 'array', 'Return type should not be \"array\".');\n  if (args) {\n    for (var i = 0; i < args.length; i++) {\n      var converter = toC[argTypes[i]];\n      if (converter) {\n        if (stack === 0) stack = stackSave();\n        cArgs[i] = converter(args[i]);\n      } else {\n        cArgs[i] = args[i];\n      }\n    }\n  }\n  var ret = func.apply(null, cArgs);\n\n  ret = convertReturnValue(ret);\n  if (stack !== 0) stackRestore(stack);\n  return ret;\n}\n\n/** @param {string=} returnType\n    @param {Array=} argTypes\n    @param {Object=} opts */\nfunction cwrap(ident, returnType, argTypes, opts) {\n  return function() {\n    return ccall(ident, returnType, argTypes, arguments, opts);\n  }\n}\n\n// We used to include malloc/free by default in the past. Show a helpful error in\n// builds with assertions.\n\nvar ALLOC_NORMAL = 0; // Tries to use _malloc()\nvar ALLOC_STACK = 1; // Lives for the duration of the current function call\n\n// allocate(): This is for internal use. You can use it yourself as well, but the interface\n//             is a little tricky (see docs right below). The reason is that it is optimized\n//             for multiple syntaxes to save space in generated code. So you should\n//             normally not use allocate(), and instead allocate memory using _malloc(),\n//             initialize it with setValue(), and so forth.\n// @slab: An array of data.\n// @allocator: How to allocate memory, see ALLOC_*\n/** @type {function((Uint8Array|Array<number>), number)} */\nfunction allocate(slab, allocator) {\n  var ret;\n  assert(typeof allocator === 'number', 'allocate no longer takes a type argument')\n  assert(typeof slab !== 'number', 'allocate no longer takes a number as arg0')\n\n  if (allocator == ALLOC_STACK) {\n    ret = stackAlloc(slab.length);\n  } else {\n    ret = _malloc(slab.length);\n  }\n\n  if (slab.subarray || slab.slice) {\n    HEAPU8.set(/** @type {!Uint8Array} */(slab), ret);\n  } else {\n    HEAPU8.set(new Uint8Array(slab), ret);\n  }\n  return ret;\n}\n\n// include: runtime_strings.js\n\n\n// runtime_strings.js: Strings related runtime functions that are part of both MINIMAL_RUNTIME and regular runtime.\n\n// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the given array that contains uint8 values, returns\n// a copy of that string as a Javascript String object.\n\nvar UTF8Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined;\n\n/**\n * @param {number} idx\n * @param {number=} maxBytesToRead\n * @return {string}\n */\nfunction UTF8ArrayToString(heap, idx, maxBytesToRead) {\n  var endIdx = idx + maxBytesToRead;\n  var endPtr = idx;\n  // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself.\n  // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage.\n  // (As a tiny code save trick, compare endPtr against endIdx using a negation, so that undefined means Infinity)\n  while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr;\n\n  if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) {\n    return UTF8Decoder.decode(heap.subarray(idx, endPtr));\n  } else {\n    var str = '';\n    // If building with TextDecoder, we have already computed the string length above, so test loop end condition against that\n    while (idx < endPtr) {\n      // For UTF8 byte structure, see:\n      // http://en.wikipedia.org/wiki/UTF-8#Description\n      // https://www.ietf.org/rfc/rfc2279.txt\n      // https://tools.ietf.org/html/rfc3629\n      var u0 = heap[idx++];\n      if (!(u0 & 0x80)) { str += String.fromCharCode(u0); continue; }\n      var u1 = heap[idx++] & 63;\n      if ((u0 & 0xE0) == 0xC0) { str += String.fromCharCode(((u0 & 31) << 6) | u1); continue; }\n      var u2 = heap[idx++] & 63;\n      if ((u0 & 0xF0) == 0xE0) {\n        u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;\n      } else {\n        if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte 0x' + u0.toString(16) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!');\n        u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63);\n      }\n\n      if (u0 < 0x10000) {\n        str += String.fromCharCode(u0);\n      } else {\n        var ch = u0 - 0x10000;\n        str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));\n      }\n    }\n  }\n  return str;\n}\n\n// Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the emscripten HEAP, returns a\n// copy of that string as a Javascript String object.\n// maxBytesToRead: an optional length that specifies the maximum number of bytes to read. You can omit\n//                 this parameter to scan the string until the first \\0 byte. If maxBytesToRead is\n//                 passed, and the string at [ptr, ptr+maxBytesToReadr[ contains a null byte in the\n//                 middle, then the string will cut short at that byte index (i.e. maxBytesToRead will\n//                 not produce a string of exact length [ptr, ptr+maxBytesToRead[)\n//                 N.B. mixing frequent uses of UTF8ToString() with and without maxBytesToRead may\n//                 throw JS JIT optimizations off, so it is worth to consider consistently using one\n//                 style or the other.\n/**\n * @param {number} ptr\n * @param {number=} maxBytesToRead\n * @return {string}\n */\nfunction UTF8ToString(ptr, maxBytesToRead) {\n  return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : '';\n}\n\n// Copies the given Javascript String object 'str' to the given byte array at address 'outIdx',\n// encoded in UTF8 form and null-terminated. The copy will require at most str.length*4+1 bytes of space in the HEAP.\n// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write.\n// Parameters:\n//   str: the Javascript string to copy.\n//   heap: the array to copy to. Each index in this array is assumed to be one 8-byte element.\n//   outIdx: The starting offset in the array to begin the copying.\n//   maxBytesToWrite: The maximum number of bytes this function can write to the array.\n//                    This count should include the null terminator,\n//                    i.e. if maxBytesToWrite=1, only the null terminator will be written and nothing else.\n//                    maxBytesToWrite=0 does not write any bytes to the output, not even the null terminator.\n// Returns the number of bytes written, EXCLUDING the null terminator.\n\nfunction stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) {\n  if (!(maxBytesToWrite > 0)) // Parameter maxBytesToWrite is not optional. Negative values, 0, null, undefined and false each don't write out any bytes.\n    return 0;\n\n  var startIdx = outIdx;\n  var endIdx = outIdx + maxBytesToWrite - 1; // -1 for string null terminator.\n  for (var i = 0; i < str.length; ++i) {\n    // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8.\n    // See http://unicode.org/faq/utf_bom.html#utf16-3\n    // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description and https://www.ietf.org/rfc/rfc2279.txt and https://tools.ietf.org/html/rfc3629\n    var u = str.charCodeAt(i); // possibly a lead surrogate\n    if (u >= 0xD800 && u <= 0xDFFF) {\n      var u1 = str.charCodeAt(++i);\n      u = 0x10000 + ((u & 0x3FF) << 10) | (u1 & 0x3FF);\n    }\n    if (u <= 0x7F) {\n      if (outIdx >= endIdx) break;\n      heap[outIdx++] = u;\n    } else if (u <= 0x7FF) {\n      if (outIdx + 1 >= endIdx) break;\n      heap[outIdx++] = 0xC0 | (u >> 6);\n      heap[outIdx++] = 0x80 | (u & 63);\n    } else if (u <= 0xFFFF) {\n      if (outIdx + 2 >= endIdx) break;\n      heap[outIdx++] = 0xE0 | (u >> 12);\n      heap[outIdx++] = 0x80 | ((u >> 6) & 63);\n      heap[outIdx++] = 0x80 | (u & 63);\n    } else {\n      if (outIdx + 3 >= endIdx) break;\n      if (u >= 0x200000) warnOnce('Invalid Unicode code point 0x' + u.toString(16) + ' encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x1FFFFF).');\n      heap[outIdx++] = 0xF0 | (u >> 18);\n      heap[outIdx++] = 0x80 | ((u >> 12) & 63);\n      heap[outIdx++] = 0x80 | ((u >> 6) & 63);\n      heap[outIdx++] = 0x80 | (u & 63);\n    }\n  }\n  // Null-terminate the pointer to the buffer.\n  heap[outIdx] = 0;\n  return outIdx - startIdx;\n}\n\n// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',\n// null-terminated and encoded in UTF8 form. The copy will require at most str.length*4+1 bytes of space in the HEAP.\n// Use the function lengthBytesUTF8 to compute the exact number of bytes (excluding null terminator) that this function will write.\n// Returns the number of bytes written, EXCLUDING the null terminator.\n\nfunction stringToUTF8(str, outPtr, maxBytesToWrite) {\n  assert(typeof maxBytesToWrite == 'number', 'stringToUTF8(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');\n  return stringToUTF8Array(str, HEAPU8,outPtr, maxBytesToWrite);\n}\n\n// Returns the number of bytes the given Javascript string takes if encoded as a UTF8 byte array, EXCLUDING the null terminator byte.\nfunction lengthBytesUTF8(str) {\n  var len = 0;\n  for (var i = 0; i < str.length; ++i) {\n    // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! So decode UTF16->UTF32->UTF8.\n    // See http://unicode.org/faq/utf_bom.html#utf16-3\n    var u = str.charCodeAt(i); // possibly a lead surrogate\n    if (u >= 0xD800 && u <= 0xDFFF) u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF);\n    if (u <= 0x7F) ++len;\n    else if (u <= 0x7FF) len += 2;\n    else if (u <= 0xFFFF) len += 3;\n    else len += 4;\n  }\n  return len;\n}\n\n// end include: runtime_strings.js\n// include: runtime_strings_extra.js\n\n\n// runtime_strings_extra.js: Strings related runtime functions that are available only in regular runtime.\n\n// Given a pointer 'ptr' to a null-terminated ASCII-encoded string in the emscripten HEAP, returns\n// a copy of that string as a Javascript String object.\n\nfunction AsciiToString(ptr) {\n  var str = '';\n  while (1) {\n    var ch = HEAPU8[((ptr++)>>0)];\n    if (!ch) return str;\n    str += String.fromCharCode(ch);\n  }\n}\n\n// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',\n// null-terminated and encoded in ASCII form. The copy will require at most str.length+1 bytes of space in the HEAP.\n\nfunction stringToAscii(str, outPtr) {\n  return writeAsciiToMemory(str, outPtr, false);\n}\n\n// Given a pointer 'ptr' to a null-terminated UTF16LE-encoded string in the emscripten HEAP, returns\n// a copy of that string as a Javascript String object.\n\nvar UTF16Decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined;\n\nfunction UTF16ToString(ptr, maxBytesToRead) {\n  assert(ptr % 2 == 0, 'Pointer passed to UTF16ToString must be aligned to two bytes!');\n  var endPtr = ptr;\n  // TextDecoder needs to know the byte length in advance, it doesn't stop on null terminator by itself.\n  // Also, use the length info to avoid running tiny strings through TextDecoder, since .subarray() allocates garbage.\n  var idx = endPtr >> 1;\n  var maxIdx = idx + maxBytesToRead / 2;\n  // If maxBytesToRead is not passed explicitly, it will be undefined, and this\n  // will always evaluate to true. This saves on code size.\n  while (!(idx >= maxIdx) && HEAPU16[idx]) ++idx;\n  endPtr = idx << 1;\n\n  if (endPtr - ptr > 32 && UTF16Decoder) {\n    return UTF16Decoder.decode(HEAPU8.subarray(ptr, endPtr));\n  } else {\n    var str = '';\n\n    // If maxBytesToRead is not passed explicitly, it will be undefined, and the for-loop's condition\n    // will always evaluate to true. The loop is then terminated on the first null char.\n    for (var i = 0; !(i >= maxBytesToRead / 2); ++i) {\n      var codeUnit = HEAP16[(((ptr)+(i*2))>>1)];\n      if (codeUnit == 0) break;\n      // fromCharCode constructs a character from a UTF-16 code unit, so we can pass the UTF16 string right through.\n      str += String.fromCharCode(codeUnit);\n    }\n\n    return str;\n  }\n}\n\n// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',\n// null-terminated and encoded in UTF16 form. The copy will require at most str.length*4+2 bytes of space in the HEAP.\n// Use the function lengthBytesUTF16() to compute the exact number of bytes (excluding null terminator) that this function will write.\n// Parameters:\n//   str: the Javascript string to copy.\n//   outPtr: Byte address in Emscripten HEAP where to write the string to.\n//   maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null\n//                    terminator, i.e. if maxBytesToWrite=2, only the null terminator will be written and nothing else.\n//                    maxBytesToWrite<2 does not write any bytes to the output, not even the null terminator.\n// Returns the number of bytes written, EXCLUDING the null terminator.\n\nfunction stringToUTF16(str, outPtr, maxBytesToWrite) {\n  assert(outPtr % 2 == 0, 'Pointer passed to stringToUTF16 must be aligned to two bytes!');\n  assert(typeof maxBytesToWrite == 'number', 'stringToUTF16(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');\n  // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed.\n  if (maxBytesToWrite === undefined) {\n    maxBytesToWrite = 0x7FFFFFFF;\n  }\n  if (maxBytesToWrite < 2) return 0;\n  maxBytesToWrite -= 2; // Null terminator.\n  var startPtr = outPtr;\n  var numCharsToWrite = (maxBytesToWrite < str.length*2) ? (maxBytesToWrite / 2) : str.length;\n  for (var i = 0; i < numCharsToWrite; ++i) {\n    // charCodeAt returns a UTF-16 encoded code unit, so it can be directly written to the HEAP.\n    var codeUnit = str.charCodeAt(i); // possibly a lead surrogate\n    HEAP16[((outPtr)>>1)] = codeUnit;\n    outPtr += 2;\n  }\n  // Null-terminate the pointer to the HEAP.\n  HEAP16[((outPtr)>>1)] = 0;\n  return outPtr - startPtr;\n}\n\n// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte.\n\nfunction lengthBytesUTF16(str) {\n  return str.length*2;\n}\n\nfunction UTF32ToString(ptr, maxBytesToRead) {\n  assert(ptr % 4 == 0, 'Pointer passed to UTF32ToString must be aligned to four bytes!');\n  var i = 0;\n\n  var str = '';\n  // If maxBytesToRead is not passed explicitly, it will be undefined, and this\n  // will always evaluate to true. This saves on code size.\n  while (!(i >= maxBytesToRead / 4)) {\n    var utf32 = HEAP32[(((ptr)+(i*4))>>2)];\n    if (utf32 == 0) break;\n    ++i;\n    // Gotcha: fromCharCode constructs a character from a UTF-16 encoded code (pair), not from a Unicode code point! So encode the code point to UTF-16 for constructing.\n    // See http://unicode.org/faq/utf_bom.html#utf16-3\n    if (utf32 >= 0x10000) {\n      var ch = utf32 - 0x10000;\n      str += String.fromCharCode(0xD800 | (ch >> 10), 0xDC00 | (ch & 0x3FF));\n    } else {\n      str += String.fromCharCode(utf32);\n    }\n  }\n  return str;\n}\n\n// Copies the given Javascript String object 'str' to the emscripten HEAP at address 'outPtr',\n// null-terminated and encoded in UTF32 form. The copy will require at most str.length*4+4 bytes of space in the HEAP.\n// Use the function lengthBytesUTF32() to compute the exact number of bytes (excluding null terminator) that this function will write.\n// Parameters:\n//   str: the Javascript string to copy.\n//   outPtr: Byte address in Emscripten HEAP where to write the string to.\n//   maxBytesToWrite: The maximum number of bytes this function can write to the array. This count should include the null\n//                    terminator, i.e. if maxBytesToWrite=4, only the null terminator will be written and nothing else.\n//                    maxBytesToWrite<4 does not write any bytes to the output, not even the null terminator.\n// Returns the number of bytes written, EXCLUDING the null terminator.\n\nfunction stringToUTF32(str, outPtr, maxBytesToWrite) {\n  assert(outPtr % 4 == 0, 'Pointer passed to stringToUTF32 must be aligned to four bytes!');\n  assert(typeof maxBytesToWrite == 'number', 'stringToUTF32(str, outPtr, maxBytesToWrite) is missing the third parameter that specifies the length of the output buffer!');\n  // Backwards compatibility: if max bytes is not specified, assume unsafe unbounded write is allowed.\n  if (maxBytesToWrite === undefined) {\n    maxBytesToWrite = 0x7FFFFFFF;\n  }\n  if (maxBytesToWrite < 4) return 0;\n  var startPtr = outPtr;\n  var endPtr = startPtr + maxBytesToWrite - 4;\n  for (var i = 0; i < str.length; ++i) {\n    // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap.\n    // See http://unicode.org/faq/utf_bom.html#utf16-3\n    var codeUnit = str.charCodeAt(i); // possibly a lead surrogate\n    if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) {\n      var trailSurrogate = str.charCodeAt(++i);\n      codeUnit = 0x10000 + ((codeUnit & 0x3FF) << 10) | (trailSurrogate & 0x3FF);\n    }\n    HEAP32[((outPtr)>>2)] = codeUnit;\n    outPtr += 4;\n    if (outPtr + 4 > endPtr) break;\n  }\n  // Null-terminate the pointer to the HEAP.\n  HEAP32[((outPtr)>>2)] = 0;\n  return outPtr - startPtr;\n}\n\n// Returns the number of bytes the given Javascript string takes if encoded as a UTF16 byte array, EXCLUDING the null terminator byte.\n\nfunction lengthBytesUTF32(str) {\n  var len = 0;\n  for (var i = 0; i < str.length; ++i) {\n    // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code unit, not a Unicode code point of the character! We must decode the string to UTF-32 to the heap.\n    // See http://unicode.org/faq/utf_bom.html#utf16-3\n    var codeUnit = str.charCodeAt(i);\n    if (codeUnit >= 0xD800 && codeUnit <= 0xDFFF) ++i; // possibly a lead surrogate, so skip over the tail surrogate.\n    len += 4;\n  }\n\n  return len;\n}\n\n// Allocate heap space for a JS string, and write it there.\n// It is the responsibility of the caller to free() that memory.\nfunction allocateUTF8(str) {\n  var size = lengthBytesUTF8(str) + 1;\n  var ret = _malloc(size);\n  if (ret) stringToUTF8Array(str, HEAP8, ret, size);\n  return ret;\n}\n\n// Allocate stack space for a JS string, and write it there.\nfunction allocateUTF8OnStack(str) {\n  var size = lengthBytesUTF8(str) + 1;\n  var ret = stackAlloc(size);\n  stringToUTF8Array(str, HEAP8, ret, size);\n  return ret;\n}\n\n// Deprecated: This function should not be called because it is unsafe and does not provide\n// a maximum length limit of how many bytes it is allowed to write. Prefer calling the\n// function stringToUTF8Array() instead, which takes in a maximum length that can be used\n// to be secure from out of bounds writes.\n/** @deprecated\n    @param {boolean=} dontAddNull */\nfunction writeStringToMemory(string, buffer, dontAddNull) {\n  warnOnce('writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!');\n\n  var /** @type {number} */ lastChar, /** @type {number} */ end;\n  if (dontAddNull) {\n    // stringToUTF8Array always appends null. If we don't want to do that, remember the\n    // character that existed at the location where the null will be placed, and restore\n    // that after the write (below).\n    end = buffer + lengthBytesUTF8(string);\n    lastChar = HEAP8[end];\n  }\n  stringToUTF8(string, buffer, Infinity);\n  if (dontAddNull) HEAP8[end] = lastChar; // Restore the value under the null character.\n}\n\nfunction writeArrayToMemory(array, buffer) {\n  assert(array.length >= 0, 'writeArrayToMemory array must have a length (should be an array or typed array)')\n  HEAP8.set(array, buffer);\n}\n\n/** @param {boolean=} dontAddNull */\nfunction writeAsciiToMemory(str, buffer, dontAddNull) {\n  for (var i = 0; i < str.length; ++i) {\n    assert(str.charCodeAt(i) === str.charCodeAt(i)&0xff);\n    HEAP8[((buffer++)>>0)] = str.charCodeAt(i);\n  }\n  // Null-terminate the pointer to the HEAP.\n  if (!dontAddNull) HEAP8[((buffer)>>0)] = 0;\n}\n\n// end include: runtime_strings_extra.js\n// Memory management\n\nfunction alignUp(x, multiple) {\n  if (x % multiple > 0) {\n    x += multiple - (x % multiple);\n  }\n  return x;\n}\n\nvar HEAP,\n/** @type {ArrayBuffer} */\n  buffer,\n/** @type {Int8Array} */\n  HEAP8,\n/** @type {Uint8Array} */\n  HEAPU8,\n/** @type {Int16Array} */\n  HEAP16,\n/** @type {Uint16Array} */\n  HEAPU16,\n/** @type {Int32Array} */\n  HEAP32,\n/** @type {Uint32Array} */\n  HEAPU32,\n/** @type {Float32Array} */\n  HEAPF32,\n/** @type {Float64Array} */\n  HEAPF64;\n\nfunction updateGlobalBufferAndViews(buf) {\n  buffer = buf;\n  Module['HEAP8'] = HEAP8 = new Int8Array(buf);\n  Module['HEAP16'] = HEAP16 = new Int16Array(buf);\n  Module['HEAP32'] = HEAP32 = new Int32Array(buf);\n  Module['HEAPU8'] = HEAPU8 = new Uint8Array(buf);\n  Module['HEAPU16'] = HEAPU16 = new Uint16Array(buf);\n  Module['HEAPU32'] = HEAPU32 = new Uint32Array(buf);\n  Module['HEAPF32'] = HEAPF32 = new Float32Array(buf);\n  Module['HEAPF64'] = HEAPF64 = new Float64Array(buf);\n}\n\nvar TOTAL_STACK = 5242880;\nif (Module['TOTAL_STACK']) assert(TOTAL_STACK === Module['TOTAL_STACK'], 'the stack size can no longer be determined at runtime')\n\nvar INITIAL_MEMORY = Module['INITIAL_MEMORY'] || 16777216;\nif (!Object.getOwnPropertyDescriptor(Module, 'INITIAL_MEMORY')) {\n  Object.defineProperty(Module, 'INITIAL_MEMORY', {\n    configurable: true,\n    get: function() {\n      abort('Module.INITIAL_MEMORY has been replaced with plain INITIAL_MEMORY (the initial value can be provided on Module, but after startup the value is only looked for on a local variable of that name)')\n    }\n  });\n}\n\nassert(INITIAL_MEMORY >= TOTAL_STACK, 'INITIAL_MEMORY should be larger than TOTAL_STACK, was ' + INITIAL_MEMORY + '! (TOTAL_STACK=' + TOTAL_STACK + ')');\n\n// check for full engine support (use string 'subarray' to avoid closure compiler confusion)\nassert(typeof Int32Array !== 'undefined' && typeof Float64Array !== 'undefined' && Int32Array.prototype.subarray !== undefined && Int32Array.prototype.set !== undefined,\n       'JS engine does not provide full typed array support');\n\n// If memory is defined in wasm, the user can't provide it.\nassert(!Module['wasmMemory'], 'Use of `wasmMemory` detected.  Use -s IMPORTED_MEMORY to define wasmMemory externally');\nassert(INITIAL_MEMORY == 16777216, 'Detected runtime INITIAL_MEMORY setting.  Use -s IMPORTED_MEMORY to define wasmMemory dynamically');\n\n// include: runtime_init_table.js\n// In regular non-RELOCATABLE mode the table is exported\n// from the wasm module and this will be assigned once\n// the exports are available.\nvar wasmTable;\n\n// end include: runtime_init_table.js\n// include: runtime_stack_check.js\n\n\n// Initializes the stack cookie. Called at the startup of main and at the startup of each thread in pthreads mode.\nfunction writeStackCookie() {\n  var max = _emscripten_stack_get_end();\n  assert((max & 3) == 0);\n  // The stack grows downwards\n  HEAPU32[(max >> 2)+1] = 0x2135467;\n  HEAPU32[(max >> 2)+2] = 0x89BACDFE;\n  // Also test the global address 0 for integrity.\n  HEAP32[0] = 0x63736d65; /* 'emsc' */\n}\n\nfunction checkStackCookie() {\n  if (ABORT) return;\n  var max = _emscripten_stack_get_end();\n  var cookie1 = HEAPU32[(max >> 2)+1];\n  var cookie2 = HEAPU32[(max >> 2)+2];\n  if (cookie1 != 0x2135467 || cookie2 != 0x89BACDFE) {\n    abort('Stack overflow! Stack cookie has been overwritten, expected hex dwords 0x89BACDFE and 0x2135467, but received 0x' + cookie2.toString(16) + ' ' + cookie1.toString(16));\n  }\n  // Also test the global address 0 for integrity.\n  if (HEAP32[0] !== 0x63736d65 /* 'emsc' */) abort('Runtime error: The application has corrupted its heap memory area (address zero)!');\n}\n\n// end include: runtime_stack_check.js\n// include: runtime_assertions.js\n\n\n// Endianness check (note: assumes compiler arch was little-endian)\n(function() {\n  var h16 = new Int16Array(1);\n  var h8 = new Int8Array(h16.buffer);\n  h16[0] = 0x6373;\n  if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian!';\n})();\n\nfunction abortFnPtrError(ptr, sig) {\n\tabort(\"Invalid function pointer \" + ptr + \" called with signature '\" + sig + \"'. Perhaps this is an invalid value (e.g. caused by calling a virtual method on a NULL pointer)? Or calling a function with an incorrect type, which will fail? (it is worth building your source files with -Werror (warnings are errors), as warnings can indicate undefined behavior which can cause this). Build with ASSERTIONS=2 for more info.\");\n}\n\n// end include: runtime_assertions.js\nvar __ATPRERUN__  = []; // functions called before the runtime is initialized\nvar __ATINIT__    = []; // functions called during startup\nvar __ATMAIN__    = []; // functions called when main() is to be run\nvar __ATEXIT__    = []; // functions called during shutdown\nvar __ATPOSTRUN__ = []; // functions called after the main() is called\n\nvar runtimeInitialized = false;\nvar runtimeExited = false;\n\n__ATINIT__.push({ func: function() { ___wasm_call_ctors() } });\n\nfunction preRun() {\n\n  if (Module['preRun']) {\n    if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']];\n    while (Module['preRun'].length) {\n      addOnPreRun(Module['preRun'].shift());\n    }\n  }\n\n  callRuntimeCallbacks(__ATPRERUN__);\n}\n\nfunction initRuntime() {\n  checkStackCookie();\n  assert(!runtimeInitialized);\n  runtimeInitialized = true;\n\n  if (!Module[\"noFSInit\"] && !FS.init.initialized) FS.init();\nTTY.init();\n  callRuntimeCallbacks(__ATINIT__);\n}\n\nfunction preMain() {\n  checkStackCookie();\n  FS.ignorePermissions = false;\n  callRuntimeCallbacks(__ATMAIN__);\n}\n\nfunction exitRuntime() {\n  checkStackCookie();\n  runtimeExited = true;\n}\n\nfunction postRun() {\n  checkStackCookie();\n\n  if (Module['postRun']) {\n    if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']];\n    while (Module['postRun'].length) {\n      addOnPostRun(Module['postRun'].shift());\n    }\n  }\n\n  callRuntimeCallbacks(__ATPOSTRUN__);\n}\n\nfunction addOnPreRun(cb) {\n  __ATPRERUN__.unshift(cb);\n}\n\nfunction addOnInit(cb) {\n  __ATINIT__.unshift(cb);\n}\n\nfunction addOnPreMain(cb) {\n  __ATMAIN__.unshift(cb);\n}\n\nfunction addOnExit(cb) {\n}\n\nfunction addOnPostRun(cb) {\n  __ATPOSTRUN__.unshift(cb);\n}\n\n// include: runtime_math.js\n\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32\n\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc\n\nassert(Math.imul, 'This browser does not support Math.imul(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill');\nassert(Math.fround, 'This browser does not support Math.fround(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill');\nassert(Math.clz32, 'This browser does not support Math.clz32(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill');\nassert(Math.trunc, 'This browser does not support Math.trunc(), build with LEGACY_VM_SUPPORT or POLYFILL_OLD_MATH_FUNCTIONS to add in a polyfill');\n\n// end include: runtime_math.js\n// A counter of dependencies for calling run(). If we need to\n// do asynchronous work before running, increment this and\n// decrement it. Incrementing must happen in a place like\n// Module.preRun (used by emcc to add file preloading).\n// Note that you can add dependencies in preRun, even though\n// it happens right before run - run will be postponed until\n// the dependencies are met.\nvar runDependencies = 0;\nvar runDependencyWatcher = null;\nvar dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled\nvar runDependencyTracking = {};\n\nfunction getUniqueRunDependency(id) {\n  var orig = id;\n  while (1) {\n    if (!runDependencyTracking[id]) return id;\n    id = orig + Math.random();\n  }\n}\n\nfunction addRunDependency(id) {\n  runDependencies++;\n\n  if (Module['monitorRunDependencies']) {\n    Module['monitorRunDependencies'](runDependencies);\n  }\n\n  if (id) {\n    assert(!runDependencyTracking[id]);\n    runDependencyTracking[id] = 1;\n    if (runDependencyWatcher === null && typeof setInterval !== 'undefined') {\n      // Check for missing dependencies every few seconds\n      runDependencyWatcher = setInterval(function() {\n        if (ABORT) {\n          clearInterval(runDependencyWatcher);\n          runDependencyWatcher = null;\n          return;\n        }\n        var shown = false;\n        for (var dep in runDependencyTracking) {\n          if (!shown) {\n            shown = true;\n            err('still waiting on run dependencies:');\n          }\n          err('dependency: ' + dep);\n        }\n        if (shown) {\n          err('(end of list)');\n        }\n      }, 10000);\n    }\n  } else {\n    err('warning: run dependency added without ID');\n  }\n}\n\nfunction removeRunDependency(id) {\n  runDependencies--;\n\n  if (Module['monitorRunDependencies']) {\n    Module['monitorRunDependencies'](runDependencies);\n  }\n\n  if (id) {\n    assert(runDependencyTracking[id]);\n    delete runDependencyTracking[id];\n  } else {\n    err('warning: run dependency removed without ID');\n  }\n  if (runDependencies == 0) {\n    if (runDependencyWatcher !== null) {\n      clearInterval(runDependencyWatcher);\n      runDependencyWatcher = null;\n    }\n    if (dependenciesFulfilled) {\n      var callback = dependenciesFulfilled;\n      dependenciesFulfilled = null;\n      callback(); // can add another dependenciesFulfilled\n    }\n  }\n}\n\nModule[\"preloadedImages\"] = {}; // maps url to image data\nModule[\"preloadedAudios\"] = {}; // maps url to audio data\n\n/** @param {string|number=} what */\nfunction abort(what) {\n  if (Module['onAbort']) {\n    Module['onAbort'](what);\n  }\n\n  what += '';\n  err(what);\n\n  ABORT = true;\n  EXITSTATUS = 1;\n\n  var output = 'abort(' + what + ') at ' + stackTrace();\n  what = output;\n\n  // Use a wasm runtime error, because a JS error might be seen as a foreign\n  // exception, which means we'd run destructors on it. We need the error to\n  // simply make the program stop.\n  var e = new WebAssembly.RuntimeError(what);\n\n  // Throw the error whether or not MODULARIZE is set because abort is used\n  // in code paths apart from instantiation where an exception is expected\n  // to be thrown when abort is called.\n  throw e;\n}\n\n// {{MEM_INITIALIZER}}\n\n// include: memoryprofiler.js\n\n\n// end include: memoryprofiler.js\n// include: URIUtils.js\n\n\nfunction hasPrefix(str, prefix) {\n  return String.prototype.startsWith ?\n      str.startsWith(prefix) :\n      str.indexOf(prefix) === 0;\n}\n\n// Prefix of data URIs emitted by SINGLE_FILE and related options.\nvar dataURIPrefix = 'data:application/octet-stream;base64,';\n\n// Indicates whether filename is a base64 data URI.\nfunction isDataURI(filename) {\n  return hasPrefix(filename, dataURIPrefix);\n}\n\nvar fileURIPrefix = \"file://\";\n\n// Indicates whether filename is delivered via file protocol (as opposed to http/https)\nfunction isFileURI(filename) {\n  return hasPrefix(filename, fileURIPrefix);\n}\n\n// end include: URIUtils.js\nfunction createExportWrapper(name, fixedasm) {\n  return function() {\n    var displayName = name;\n    var asm = fixedasm;\n    if (!fixedasm) {\n      asm = Module['asm'];\n    }\n    assert(runtimeInitialized, 'native function `' + displayName + '` called before runtime initialization');\n    assert(!runtimeExited, 'native function `' + displayName + '` called after runtime exit (use NO_EXIT_RUNTIME to keep it alive after main() exits)');\n    if (!asm[name]) {\n      assert(asm[name], 'exported native function `' + displayName + '` not found');\n    }\n    return asm[name].apply(null, arguments);\n  };\n}\n\nvar wasmBinaryFile = 'LowResNX120.wasm';\nif (!isDataURI(wasmBinaryFile)) {\n  wasmBinaryFile = locateFile(wasmBinaryFile);\n}\n\nfunction getBinary(file) {\n  try {\n    if (file == wasmBinaryFile && wasmBinary) {\n      return new Uint8Array(wasmBinary);\n    }\n    if (readBinary) {\n      return readBinary(file);\n    } else {\n      throw \"both async and sync fetching of the wasm failed\";\n    }\n  }\n  catch (err) {\n    abort(err);\n  }\n}\n\nfunction getBinaryPromise() {\n  // If we don't have the binary yet, try to to load it asynchronously.\n  // Fetch has some additional restrictions over XHR, like it can't be used on a file:// url.\n  // See https://github.com/github/fetch/pull/92#issuecomment-140665932\n  // Cordova or Electron apps are typically loaded from a file:// url.\n  // So use fetch if it is available and the url is not a file, otherwise fall back to XHR.\n  if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) {\n    if (typeof fetch === 'function'\n      && !isFileURI(wasmBinaryFile)\n    ) {\n      return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function(response) {\n        if (!response['ok']) {\n          throw \"failed to load wasm binary file at '\" + wasmBinaryFile + \"'\";\n        }\n        return response['arrayBuffer']();\n      }).catch(function () {\n          return getBinary(wasmBinaryFile);\n      });\n    }\n    else {\n      if (readAsync) {\n        // fetch is not available or url is file => try XHR (readAsync uses XHR internally)\n        return new Promise(function(resolve, reject) {\n          readAsync(wasmBinaryFile, function(response) { resolve(new Uint8Array(/** @type{!ArrayBuffer} */(response))) }, reject)\n        });\n      }\n    }\n  }\n    \n  // Otherwise, getBinary should be able to get it synchronously\n  return Promise.resolve().then(function() { return getBinary(wasmBinaryFile); });\n}\n\n// Create the wasm instance.\n// Receives the wasm imports, returns the exports.\nfunction createWasm() {\n  // prepare imports\n  var info = {\n    'env': asmLibraryArg,\n    'wasi_snapshot_preview1': asmLibraryArg,\n  };\n  // Load the wasm module and create an instance of using native support in the JS engine.\n  // handle a generated wasm instance, receiving its exports and\n  // performing other necessary setup\n  /** @param {WebAssembly.Module=} module*/\n  function receiveInstance(instance, module) {\n    var exports = instance.exports;\n\n    Module['asm'] = exports;\n\n    wasmMemory = Module['asm']['memory'];\n    assert(wasmMemory, \"memory not found in wasm exports\");\n    // This assertion doesn't hold when emscripten is run in --post-link\n    // mode.\n    // TODO(sbc): Read INITIAL_MEMORY out of the wasm file in post-link mode.\n    //assert(wasmMemory.buffer.byteLength === 16777216);\n    updateGlobalBufferAndViews(wasmMemory.buffer);\n\n    wasmTable = Module['asm']['__indirect_function_table'];\n    assert(wasmTable, \"table not found in wasm exports\");\n\n    removeRunDependency('wasm-instantiate');\n  }\n  // we can't run yet (except in a pthread, where we have a custom sync instantiator)\n  addRunDependency('wasm-instantiate');\n\n  // Async compilation can be confusing when an error on the page overwrites Module\n  // (for example, if the order of elements is wrong, and the one defining Module is\n  // later), so we save Module and check it later.\n  var trueModule = Module;\n  function receiveInstantiatedSource(output) {\n    // 'output' is a WebAssemblyInstantiatedSource object which has both the module and instance.\n    // receiveInstance() will swap in the exports (to Module.asm) so they can be called\n    assert(Module === trueModule, 'the Module object should not be replaced during async compilation - perhaps the order of HTML elements is wrong?');\n    trueModule = null;\n    // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line.\n    // When the regression is fixed, can restore the above USE_PTHREADS-enabled path.\n    receiveInstance(output['instance']);\n  }\n\n  function instantiateArrayBuffer(receiver) {\n    return getBinaryPromise().then(function(binary) {\n      return WebAssembly.instantiate(binary, info);\n    }).then(receiver, function(reason) {\n      err('failed to asynchronously prepare wasm: ' + reason);\n\n      // Warn on some common problems.\n      if (isFileURI(wasmBinaryFile)) {\n        err('warning: Loading from a file URI (' + wasmBinaryFile + ') is not supported in most browsers. See https://emscripten.org/docs/getting_started/FAQ.html#how-do-i-run-a-local-webserver-for-testing-why-does-my-program-stall-in-downloading-or-preparing');\n      }\n      abort(reason);\n    });\n  }\n\n  // Prefer streaming instantiation if available.\n  function instantiateAsync() {\n    if (!wasmBinary &&\n        typeof WebAssembly.instantiateStreaming === 'function' &&\n        !isDataURI(wasmBinaryFile) &&\n        // Don't use streaming for file:// delivered objects in a webview, fetch them synchronously.\n        !isFileURI(wasmBinaryFile) &&\n        typeof fetch === 'function') {\n      return fetch(wasmBinaryFile, { credentials: 'same-origin' }).then(function (response) {\n        var result = WebAssembly.instantiateStreaming(response, info);\n        return result.then(receiveInstantiatedSource, function(reason) {\n            // We expect the most common failure cause to be a bad MIME type for the binary,\n            // in which case falling back to ArrayBuffer instantiation should work.\n            err('wasm streaming compile failed: ' + reason);\n            err('falling back to ArrayBuffer instantiation');\n            return instantiateArrayBuffer(receiveInstantiatedSource);\n          });\n      });\n    } else {\n      return instantiateArrayBuffer(receiveInstantiatedSource);\n    }\n  }\n\n  // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback\n  // to manually instantiate the Wasm module themselves. This allows pages to run the instantiation parallel\n  // to any other async startup actions they are performing.\n  if (Module['instantiateWasm']) {\n    try {\n      var exports = Module['instantiateWasm'](info, receiveInstance);\n      return exports;\n    } catch(e) {\n      err('Module.instantiateWasm callback failed with error: ' + e);\n      return false;\n    }\n  }\n\n  instantiateAsync();\n  return {}; // no exports yet; we'll fill them in later\n}\n\n// Globals used by JS i64 conversions (see makeSetValue)\nvar tempDouble;\nvar tempI64;\n\n// === Body ===\n\nvar ASM_CONSTS = {\n  65204: function($0) {var str = UTF8ToString($0) + '\\n\\n' + 'Abort/Retry/Ignore/AlwaysIgnore? [ariA] :'; var reply = window.prompt(str, \"i\"); if (reply === null) { reply = \"i\"; } return allocate(intArrayFromString(reply), 'i8', ALLOC_NORMAL);},  \n 65429: function($0, $1, $2) {var w = $0; var h = $1; var pixels = $2; if (!Module['SDL2']) Module['SDL2'] = {}; var SDL2 = Module['SDL2']; if (SDL2.ctxCanvas !== Module['canvas']) { SDL2.ctx = Module['createContext'](Module['canvas'], false, true); SDL2.ctxCanvas = Module['canvas']; } if (SDL2.w !== w || SDL2.h !== h || SDL2.imageCtx !== SDL2.ctx) { SDL2.image = SDL2.ctx.createImageData(w, h); SDL2.w = w; SDL2.h = h; SDL2.imageCtx = SDL2.ctx; } var data = SDL2.image.data; var src = pixels >> 2; var dst = 0; var num; if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) { num = data.length; while (dst < num) { var val = HEAP32[src]; data[dst ] = val & 0xff; data[dst+1] = (val >> 8) & 0xff; data[dst+2] = (val >> 16) & 0xff; data[dst+3] = 0xff; src++; dst += 4; } } else { if (SDL2.data32Data !== data) { SDL2.data32 = new Int32Array(data.buffer); SDL2.data8 = new Uint8Array(data.buffer); } var data32 = SDL2.data32; num = data32.length; data32.set(HEAP32.subarray(src, src + num)); var data8 = SDL2.data8; var i = 3; var j = i + 4*num; if (num % 8 == 0) { while (i < j) { data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; data8[i] = 0xff; i = i + 4 | 0; } } else { while (i < j) { data8[i] = 0xff; i = i + 4 | 0; } } } SDL2.ctx.putImageData(SDL2.image, 0, 0); return 0;},  \n 66884: function($0, $1, $2, $3, $4) {var w = $0; var h = $1; var hot_x = $2; var hot_y = $3; var pixels = $4; var canvas = document.createElement(\"canvas\"); canvas.width = w; canvas.height = h; var ctx = canvas.getContext(\"2d\"); var image = ctx.createImageData(w, h); var data = image.data; var src = pixels >> 2; var dst = 0; var num; if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) { num = data.length; while (dst < num) { var val = HEAP32[src]; data[dst ] = val & 0xff; data[dst+1] = (val >> 8) & 0xff; data[dst+2] = (val >> 16) & 0xff; data[dst+3] = (val >> 24) & 0xff; src++; dst += 4; } } else { var data32 = new Int32Array(data.buffer); num = data32.length; data32.set(HEAP32.subarray(src, src + num)); } ctx.putImageData(image, 0, 0); var url = hot_x === 0 && hot_y === 0 ? \"url(\" + canvas.toDataURL() + \"), auto\" : \"url(\" + canvas.toDataURL() + \") \" + hot_x + \" \" + hot_y + \", auto\"; var urlBuf = _malloc(url.length + 1); stringToUTF8(url, urlBuf, url.length + 1); return urlBuf;},  \n 67873: function($0) {if (Module['canvas']) { Module['canvas'].style['cursor'] = UTF8ToString($0); } return 0;},  \n 67966: function() {if (Module['canvas']) { Module['canvas'].style['cursor'] = 'none'; }},  \n 68035: function() {return screen.width;},  \n 68060: function() {return screen.height;},  \n 68086: function() {return window.innerWidth;},  \n 68116: function() {return window.innerHeight;},  \n 68147: function($0) {if (typeof setWindowTitle !== 'undefined') { setWindowTitle(UTF8ToString($0)); } return 0;},  \n 68242: function() {if (typeof(AudioContext) !== 'undefined') { return 1; } else if (typeof(webkitAudioContext) !== 'undefined') { return 1; } return 0;},  \n 68379: function() {if ((typeof(navigator.mediaDevices) !== 'undefined') && (typeof(navigator.mediaDevices.getUserMedia) !== 'undefined')) { return 1; } else if (typeof(navigator.webkitGetUserMedia) !== 'undefined') { return 1; } return 0;},  \n 68603: function($0) {if(typeof(Module['SDL2']) === 'undefined') { Module['SDL2'] = {}; } var SDL2 = Module['SDL2']; if (!$0) { SDL2.audio = {}; } else { SDL2.capture = {}; } if (!SDL2.audioContext) { if (typeof(AudioContext) !== 'undefined') { SDL2.audioContext = new AudioContext(); } else if (typeof(webkitAudioContext) !== 'undefined') { SDL2.audioContext = new webkitAudioContext(); } if (SDL2.audioContext) { autoResumeAudioContext(SDL2.audioContext); } } return SDL2.audioContext === undefined ? -1 : 0;},  \n 69096: function() {var SDL2 = Module['SDL2']; return SDL2.audioContext.sampleRate;},  \n 69164: function($0, $1, $2, $3) {var SDL2 = Module['SDL2']; var have_microphone = function(stream) { if (SDL2.capture.silenceTimer !== undefined) { clearTimeout(SDL2.capture.silenceTimer); SDL2.capture.silenceTimer = undefined; } SDL2.capture.mediaStreamNode = SDL2.audioContext.createMediaStreamSource(stream); SDL2.capture.scriptProcessorNode = SDL2.audioContext.createScriptProcessor($1, $0, 1); SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) { if ((SDL2 === undefined) || (SDL2.capture === undefined)) { return; } audioProcessingEvent.outputBuffer.getChannelData(0).fill(0.0); SDL2.capture.currentCaptureBuffer = audioProcessingEvent.inputBuffer; dynCall('vi', $2, [$3]); }; SDL2.capture.mediaStreamNode.connect(SDL2.capture.scriptProcessorNode); SDL2.capture.scriptProcessorNode.connect(SDL2.audioContext.destination); SDL2.capture.stream = stream; }; var no_microphone = function(error) { }; SDL2.capture.silenceBuffer = SDL2.audioContext.createBuffer($0, $1, SDL2.audioContext.sampleRate); SDL2.capture.silenceBuffer.getChannelData(0).fill(0.0); var silence_callback = function() { SDL2.capture.currentCaptureBuffer = SDL2.capture.silenceBuffer; dynCall('vi', $2, [$3]); }; SDL2.capture.silenceTimer = setTimeout(silence_callback, ($1 / SDL2.audioContext.sampleRate) * 1000); if ((navigator.mediaDevices !== undefined) && (navigator.mediaDevices.getUserMedia !== undefined)) { navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then(have_microphone).catch(no_microphone); } else if (navigator.webkitGetUserMedia !== undefined) { navigator.webkitGetUserMedia({ audio: true, video: false }, have_microphone, no_microphone); }},  \n 70816: function($0, $1, $2, $3) {var SDL2 = Module['SDL2']; SDL2.audio.scriptProcessorNode = SDL2.audioContext['createScriptProcessor']($1, 0, $0); SDL2.audio.scriptProcessorNode['onaudioprocess'] = function (e) { if ((SDL2 === undefined) || (SDL2.audio === undefined)) { return; } SDL2.audio.currentOutputBuffer = e['outputBuffer']; dynCall('vi', $2, [$3]); }; SDL2.audio.scriptProcessorNode['connect'](SDL2.audioContext['destination']);},  \n 71226: function($0, $1) {var SDL2 = Module['SDL2']; var numChannels = SDL2.capture.currentCaptureBuffer.numberOfChannels; for (var c = 0; c < numChannels; ++c) { var channelData = SDL2.capture.currentCaptureBuffer.getChannelData(c); if (channelData.length != $1) { throw 'Web Audio capture buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; } if (numChannels == 1) { for (var j = 0; j < $1; ++j) { setValue($0 + (j * 4), channelData[j], 'float'); } } else { for (var j = 0; j < $1; ++j) { setValue($0 + (((j * numChannels) + c) * 4), channelData[j], 'float'); } } }},  \n 71831: function($0, $1) {var SDL2 = Module['SDL2']; var numChannels = SDL2.audio.currentOutputBuffer['numberOfChannels']; for (var c = 0; c < numChannels; ++c) { var channelData = SDL2.audio.currentOutputBuffer['getChannelData'](c); if (channelData.length != $1) { throw 'Web Audio output buffer length mismatch! Destination size: ' + channelData.length + ' samples vs expected ' + $1 + ' samples!'; } for (var j = 0; j < $1; ++j) { channelData[j] = HEAPF32[$0 + ((j*numChannels + c) << 2) >> 2]; } }},  \n 72311: function($0) {var SDL2 = Module['SDL2']; if ($0) { if (SDL2.capture.silenceTimer !== undefined) { clearTimeout(SDL2.capture.silenceTimer); } if (SDL2.capture.stream !== undefined) { var tracks = SDL2.capture.stream.getAudioTracks(); for (var i = 0; i < tracks.length; i++) { SDL2.capture.stream.removeTrack(tracks[i]); } SDL2.capture.stream = undefined; } if (SDL2.capture.scriptProcessorNode !== undefined) { SDL2.capture.scriptProcessorNode.onaudioprocess = function(audioProcessingEvent) {}; SDL2.capture.scriptProcessorNode.disconnect(); SDL2.capture.scriptProcessorNode = undefined; } if (SDL2.capture.mediaStreamNode !== undefined) { SDL2.capture.mediaStreamNode.disconnect(); SDL2.capture.mediaStreamNode = undefined; } if (SDL2.capture.silenceBuffer !== undefined) { SDL2.capture.silenceBuffer = undefined } SDL2.capture = undefined; } else { if (SDL2.audio.scriptProcessorNode != undefined) { SDL2.audio.scriptProcessorNode.disconnect(); SDL2.audio.scriptProcessorNode = undefined; } SDL2.audio = undefined; } if ((SDL2.audioContext !== undefined) && (SDL2.audio === undefined) && (SDL2.capture === undefined)) { SDL2.audioContext.close(); SDL2.audioContext = undefined; }}\n};\n\n\n\n\n\n\n  function listenOnce(object, event, func) {\n      object.addEventListener(event, func, { 'once': true });\n    }\n  function autoResumeAudioContext(ctx, elements) {\n      if (!elements) {\n        elements = [document, document.getElementById('canvas')];\n      }\n      ['keydown', 'mousedown', 'touchstart'].forEach(function(event) {\n        elements.forEach(function(element) {\n          if (element) {\n            listenOnce(element, event, function() {\n              if (ctx.state === 'suspended') ctx.resume();\n            });\n          }\n        });\n      });\n    }\n\n  function callRuntimeCallbacks(callbacks) {\n      while(callbacks.length > 0) {\n        var callback = callbacks.shift();\n        if (typeof callback == 'function') {\n          callback(Module); // Pass the module as the first argument.\n          continue;\n        }\n        var func = callback.func;\n        if (typeof func === 'number') {\n          if (callback.arg === undefined) {\n            wasmTable.get(func)();\n          } else {\n            wasmTable.get(func)(callback.arg);\n          }\n        } else {\n          func(callback.arg === undefined ? null : callback.arg);\n        }\n      }\n    }\n\n  function demangle(func) {\n      warnOnce('warning: build with  -s DEMANGLE_SUPPORT=1  to link in libcxxabi demangling');\n      return func;\n    }\n\n  function demangleAll(text) {\n      var regex =\n        /\\b_Z[\\w\\d_]+/g;\n      return text.replace(regex,\n        function(x) {\n          var y = demangle(x);\n          return x === y ? x : (y + ' [' + x + ']');\n        });\n    }\n\n  function dynCallLegacy(sig, ptr, args) {\n      assert(('dynCall_' + sig) in Module, 'bad function pointer type - no table for sig \\'' + sig + '\\'');\n      if (args && args.length) {\n        // j (64-bit integer) must be passed in as two numbers [low 32, high 32].\n        assert(args.length === sig.substring(1).replace(/j/g, '--').length);\n      } else {\n        assert(sig.length == 1);\n      }\n      var f = Module[\"dynCall_\" + sig];\n      return args && args.length ? f.apply(null, [ptr].concat(args)) : f.call(null, ptr);\n    }\n  function dynCall(sig, ptr, args) {\n      // Without WASM_BIGINT support we cannot directly call function with i64 as\n      // part of thier signature, so we rely the dynCall functions generated by\n      // wasm-emscripten-finalize\n      if (sig.indexOf('j') != -1) {\n        return dynCallLegacy(sig, ptr, args);\n      }\n      assert(wasmTable.get(ptr), 'missing table entry in dynCall: ' + ptr);\n      return wasmTable.get(ptr).apply(null, args)\n    }\n\n  function jsStackTrace() {\n      var error = new Error();\n      if (!error.stack) {\n        // IE10+ special cases: It does have callstack info, but it is only populated if an Error object is thrown,\n        // so try that as a special-case.\n        try {\n          throw new Error();\n        } catch(e) {\n          error = e;\n        }\n        if (!error.stack) {\n          return '(no stack trace available)';\n        }\n      }\n      return error.stack.toString();\n    }\n\n  function stackTrace() {\n      var js = jsStackTrace();\n      if (Module['extraStackTrace']) js += '\\n' + Module['extraStackTrace']();\n      return demangleAll(js);\n    }\n\n  function ___assert_fail(condition, filename, line, func) {\n      abort('Assertion failed: ' + UTF8ToString(condition) + ', at: ' + [filename ? UTF8ToString(filename) : 'unknown filename', line, func ? UTF8ToString(func) : 'unknown function']);\n    }\n\n  function setErrNo(value) {\n      HEAP32[((___errno_location())>>2)] = value;\n      return value;\n    }\n  \n  var PATH={splitPath:function(filename) {\n        var splitPathRe = /^(\\/?|)([\\s\\S]*?)((?:\\.{1,2}|[^\\/]+?|)(\\.[^.\\/]*|))(?:[\\/]*)$/;\n        return splitPathRe.exec(filename).slice(1);\n      },normalizeArray:function(parts, allowAboveRoot) {\n        // if the path tries to go above the root, `up` ends up > 0\n        var up = 0;\n        for (var i = parts.length - 1; i >= 0; i--) {\n          var last = parts[i];\n          if (last === '.') {\n            parts.splice(i, 1);\n          } else if (last === '..') {\n            parts.splice(i, 1);\n            up++;\n          } else if (up) {\n            parts.splice(i, 1);\n            up--;\n          }\n        }\n        // if the path is allowed to go above the root, restore leading ..s\n        if (allowAboveRoot) {\n          for (; up; up--) {\n            parts.unshift('..');\n          }\n        }\n        return parts;\n      },normalize:function(path) {\n        var isAbsolute = path.charAt(0) === '/',\n            trailingSlash = path.substr(-1) === '/';\n        // Normalize the path\n        path = PATH.normalizeArray(path.split('/').filter(function(p) {\n          return !!p;\n        }), !isAbsolute).join('/');\n        if (!path && !isAbsolute) {\n          path = '.';\n        }\n        if (path && trailingSlash) {\n          path += '/';\n        }\n        return (isAbsolute ? '/' : '') + path;\n      },dirname:function(path) {\n        var result = PATH.splitPath(path),\n            root = result[0],\n            dir = result[1];\n        if (!root && !dir) {\n          // No dirname whatsoever\n          return '.';\n        }\n        if (dir) {\n          // It has a dirname, strip trailing slash\n          dir = dir.substr(0, dir.length - 1);\n        }\n        return root + dir;\n      },basename:function(path) {\n        // EMSCRIPTEN return '/'' for '/', not an empty string\n        if (path === '/') return '/';\n        path = PATH.normalize(path);\n        path = path.replace(/\\/$/, \"\");\n        var lastSlash = path.lastIndexOf('/');\n        if (lastSlash === -1) return path;\n        return path.substr(lastSlash+1);\n      },extname:function(path) {\n        return PATH.splitPath(path)[3];\n      },join:function() {\n        var paths = Array.prototype.slice.call(arguments, 0);\n        return PATH.normalize(paths.join('/'));\n      },join2:function(l, r) {\n        return PATH.normalize(l + '/' + r);\n      }};\n  \n  function getRandomDevice() {\n      if (typeof crypto === 'object' && typeof crypto['getRandomValues'] === 'function') {\n        // for modern web browsers\n        var randomBuffer = new Uint8Array(1);\n        return function() { crypto.getRandomValues(randomBuffer); return randomBuffer[0]; };\n      } else\n      if (ENVIRONMENT_IS_NODE) {\n        // for nodejs with or without crypto support included\n        try {\n          var crypto_module = require('crypto');\n          // nodejs has crypto support\n          return function() { return crypto_module['randomBytes'](1)[0]; };\n        } catch (e) {\n          // nodejs doesn't have crypto support\n        }\n      }\n      // we couldn't find a proper implementation, as Math.random() is not suitable for /dev/random, see emscripten-core/emscripten/pull/7096\n      return function() { abort(\"no cryptographic support found for randomDevice. consider polyfilling it if you want to use something insecure like Math.random(), e.g. put this in a --pre-js: var crypto = { getRandomValues: function(array) { for (var i = 0; i < array.length; i++) array[i] = (Math.random()*256)|0 } };\"); };\n    }\n  \n  var PATH_FS={resolve:function() {\n        var resolvedPath = '',\n          resolvedAbsolute = false;\n        for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {\n          var path = (i >= 0) ? arguments[i] : FS.cwd();\n          // Skip empty and invalid entries\n          if (typeof path !== 'string') {\n            throw new TypeError('Arguments to path.resolve must be strings');\n          } else if (!path) {\n            return ''; // an invalid portion invalidates the whole thing\n          }\n          resolvedPath = path + '/' + resolvedPath;\n          resolvedAbsolute = path.charAt(0) === '/';\n        }\n        // At this point the path should be resolved to a full absolute path, but\n        // handle relative paths to be safe (might happen when process.cwd() fails)\n        resolvedPath = PATH.normalizeArray(resolvedPath.split('/').filter(function(p) {\n          return !!p;\n        }), !resolvedAbsolute).join('/');\n        return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';\n      },relative:function(from, to) {\n        from = PATH_FS.resolve(from).substr(1);\n        to = PATH_FS.resolve(to).substr(1);\n        function trim(arr) {\n          var start = 0;\n          for (; start < arr.length; start++) {\n            if (arr[start] !== '') break;\n          }\n          var end = arr.length - 1;\n          for (; end >= 0; end--) {\n            if (arr[end] !== '') break;\n          }\n          if (start > end) return [];\n          return arr.slice(start, end - start + 1);\n        }\n        var fromParts = trim(from.split('/'));\n        var toParts = trim(to.split('/'));\n        var length = Math.min(fromParts.length, toParts.length);\n        var samePartsLength = length;\n        for (var i = 0; i < length; i++) {\n          if (fromParts[i] !== toParts[i]) {\n            samePartsLength = i;\n            break;\n          }\n        }\n        var outputParts = [];\n        for (var i = samePartsLength; i < fromParts.length; i++) {\n          outputParts.push('..');\n        }\n        outputParts = outputParts.concat(toParts.slice(samePartsLength));\n        return outputParts.join('/');\n      }};\n  \n  var TTY={ttys:[],init:function () {\n        // https://github.com/emscripten-core/emscripten/pull/1555\n        // if (ENVIRONMENT_IS_NODE) {\n        //   // currently, FS.init does not distinguish if process.stdin is a file or TTY\n        //   // device, it always assumes it's a TTY device. because of this, we're forcing\n        //   // process.stdin to UTF8 encoding to at least make stdin reading compatible\n        //   // with text files until FS.init can be refactored.\n        //   process['stdin']['setEncoding']('utf8');\n        // }\n      },shutdown:function() {\n        // https://github.com/emscripten-core/emscripten/pull/1555\n        // if (ENVIRONMENT_IS_NODE) {\n        //   // inolen: any idea as to why node -e 'process.stdin.read()' wouldn't exit immediately (with process.stdin being a tty)?\n        //   // isaacs: because now it's reading from the stream, you've expressed interest in it, so that read() kicks off a _read() which creates a ReadReq operation\n        //   // inolen: I thought read() in that case was a synchronous operation that just grabbed some amount of buffered data if it exists?\n        //   // isaacs: it is. but it also triggers a _read() call, which calls readStart() on the handle\n        //   // isaacs: do process.stdin.pause() and i'd think it'd probably close the pending call\n        //   process['stdin']['pause']();\n        // }\n      },register:function(dev, ops) {\n        TTY.ttys[dev] = { input: [], output: [], ops: ops };\n        FS.registerDevice(dev, TTY.stream_ops);\n      },stream_ops:{open:function(stream) {\n          var tty = TTY.ttys[stream.node.rdev];\n          if (!tty) {\n            throw new FS.ErrnoError(43);\n          }\n          stream.tty = tty;\n          stream.seekable = false;\n        },close:function(stream) {\n          // flush any pending line data\n          stream.tty.ops.flush(stream.tty);\n        },flush:function(stream) {\n          stream.tty.ops.flush(stream.tty);\n        },read:function(stream, buffer, offset, length, pos /* ignored */) {\n          if (!stream.tty || !stream.tty.ops.get_char) {\n            throw new FS.ErrnoError(60);\n          }\n          var bytesRead = 0;\n          for (var i = 0; i < length; i++) {\n            var result;\n            try {\n              result = stream.tty.ops.get_char(stream.tty);\n            } catch (e) {\n              throw new FS.ErrnoError(29);\n            }\n            if (result === undefined && bytesRead === 0) {\n              throw new FS.ErrnoError(6);\n            }\n            if (result === null || result === undefined) break;\n            bytesRead++;\n            buffer[offset+i] = result;\n          }\n          if (bytesRead) {\n            stream.node.timestamp = Date.now();\n          }\n          return bytesRead;\n        },write:function(stream, buffer, offset, length, pos) {\n          if (!stream.tty || !stream.tty.ops.put_char) {\n            throw new FS.ErrnoError(60);\n          }\n          try {\n            for (var i = 0; i < length; i++) {\n              stream.tty.ops.put_char(stream.tty, buffer[offset+i]);\n            }\n          } catch (e) {\n            throw new FS.ErrnoError(29);\n          }\n          if (length) {\n            stream.node.timestamp = Date.now();\n          }\n          return i;\n        }},default_tty_ops:{get_char:function(tty) {\n          if (!tty.input.length) {\n            var result = null;\n            if (ENVIRONMENT_IS_NODE) {\n              // we will read data by chunks of BUFSIZE\n              var BUFSIZE = 256;\n              var buf = Buffer.alloc ? Buffer.alloc(BUFSIZE) : new Buffer(BUFSIZE);\n              var bytesRead = 0;\n  \n              try {\n                bytesRead = nodeFS.readSync(process.stdin.fd, buf, 0, BUFSIZE, null);\n              } catch(e) {\n                // Cross-platform differences: on Windows, reading EOF throws an exception, but on other OSes,\n                // reading EOF returns 0. Uniformize behavior by treating the EOF exception to return 0.\n                if (e.toString().indexOf('EOF') != -1) bytesRead = 0;\n                else throw e;\n              }\n  \n              if (bytesRead > 0) {\n                result = buf.slice(0, bytesRead).toString('utf-8');\n              } else {\n                result = null;\n              }\n            } else\n            if (typeof window != 'undefined' &&\n              typeof window.prompt == 'function') {\n              // Browser.\n              result = window.prompt('Input: ');  // returns null on cancel\n              if (result !== null) {\n                result += '\\n';\n              }\n            } else if (typeof readline == 'function') {\n              // Command line.\n              result = readline();\n              if (result !== null) {\n                result += '\\n';\n              }\n            }\n            if (!result) {\n              return null;\n            }\n            tty.input = intArrayFromString(result, true);\n          }\n          return tty.input.shift();\n        },put_char:function(tty, val) {\n          if (val === null || val === 10) {\n            out(UTF8ArrayToString(tty.output, 0));\n            tty.output = [];\n          } else {\n            if (val != 0) tty.output.push(val); // val == 0 would cut text output off in the middle.\n          }\n        },flush:function(tty) {\n          if (tty.output && tty.output.length > 0) {\n            out(UTF8ArrayToString(tty.output, 0));\n            tty.output = [];\n          }\n        }},default_tty1_ops:{put_char:function(tty, val) {\n          if (val === null || val === 10) {\n            err(UTF8ArrayToString(tty.output, 0));\n            tty.output = [];\n          } else {\n            if (val != 0) tty.output.push(val);\n          }\n        },flush:function(tty) {\n          if (tty.output && tty.output.length > 0) {\n            err(UTF8ArrayToString(tty.output, 0));\n            tty.output = [];\n          }\n        }}};\n  \n  function mmapAlloc(size) {\n      var alignedSize = alignMemory(size, 16384);\n      var ptr = _malloc(alignedSize);\n      while (size < alignedSize) HEAP8[ptr + size++] = 0;\n      return ptr;\n    }\n  var MEMFS={ops_table:null,mount:function(mount) {\n        return MEMFS.createNode(null, '/', 16384 | 511 /* 0777 */, 0);\n      },createNode:function(parent, name, mode, dev) {\n        if (FS.isBlkdev(mode) || FS.isFIFO(mode)) {\n          // no supported\n          throw new FS.ErrnoError(63);\n        }\n        if (!MEMFS.ops_table) {\n          MEMFS.ops_table = {\n            dir: {\n              node: {\n                getattr: MEMFS.node_ops.getattr,\n                setattr: MEMFS.node_ops.setattr,\n                lookup: MEMFS.node_ops.lookup,\n                mknod: MEMFS.node_ops.mknod,\n                rename: MEMFS.node_ops.rename,\n                unlink: MEMFS.node_ops.unlink,\n                rmdir: MEMFS.node_ops.rmdir,\n                readdir: MEMFS.node_ops.readdir,\n                symlink: MEMFS.node_ops.symlink\n              },\n              stream: {\n                llseek: MEMFS.stream_ops.llseek\n              }\n            },\n            file: {\n              node: {\n                getattr: MEMFS.node_ops.getattr,\n                setattr: MEMFS.node_ops.setattr\n              },\n              stream: {\n                llseek: MEMFS.stream_ops.llseek,\n                read: MEMFS.stream_ops.read,\n                write: MEMFS.stream_ops.write,\n                allocate: MEMFS.stream_ops.allocate,\n                mmap: MEMFS.stream_ops.mmap,\n                msync: MEMFS.stream_ops.msync\n              }\n            },\n            link: {\n              node: {\n                getattr: MEMFS.node_ops.getattr,\n                setattr: MEMFS.node_ops.setattr,\n                readlink: MEMFS.node_ops.readlink\n              },\n              stream: {}\n            },\n            chrdev: {\n              node: {\n                getattr: MEMFS.node_ops.getattr,\n                setattr: MEMFS.node_ops.setattr\n              },\n              stream: FS.chrdev_stream_ops\n            }\n          };\n        }\n        var node = FS.createNode(parent, name, mode, dev);\n        if (FS.isDir(node.mode)) {\n          node.node_ops = MEMFS.ops_table.dir.node;\n          node.stream_ops = MEMFS.ops_table.dir.stream;\n          node.contents = {};\n        } else if (FS.isFile(node.mode)) {\n          node.node_ops = MEMFS.ops_table.file.node;\n          node.stream_ops = MEMFS.ops_table.file.stream;\n          node.usedBytes = 0; // The actual number of bytes used in the typed array, as opposed to contents.length which gives the whole capacity.\n          // When the byte data of the file is populated, this will point to either a typed array, or a normal JS array. Typed arrays are preferred\n          // for performance, and used by default. However, typed arrays are not resizable like normal JS arrays are, so there is a small disk size\n          // penalty involved for appending file writes that continuously grow a file similar to std::vector capacity vs used -scheme.\n          node.contents = null; \n        } else if (FS.isLink(node.mode)) {\n          node.node_ops = MEMFS.ops_table.link.node;\n          node.stream_ops = MEMFS.ops_table.link.stream;\n        } else if (FS.isChrdev(node.mode)) {\n          node.node_ops = MEMFS.ops_table.chrdev.node;\n          node.stream_ops = MEMFS.ops_table.chrdev.stream;\n        }\n        node.timestamp = Date.now();\n        // add the new node to the parent\n        if (parent) {\n          parent.contents[name] = node;\n          parent.timestamp = node.timestamp;\n        }\n        return node;\n      },getFileDataAsTypedArray:function(node) {\n        if (!node.contents) return new Uint8Array(0);\n        if (node.contents.subarray) return node.contents.subarray(0, node.usedBytes); // Make sure to not return excess unused bytes.\n        return new Uint8Array(node.contents);\n      },expandFileStorage:function(node, newCapacity) {\n        var prevCapacity = node.contents ? node.contents.length : 0;\n        if (prevCapacity >= newCapacity) return; // No need to expand, the storage was already large enough.\n        // Don't expand strictly to the given requested limit if it's only a very small increase, but instead geometrically grow capacity.\n        // For small filesizes (<1MB), perform size*2 geometric increase, but for large sizes, do a much more conservative size*1.125 increase to\n        // avoid overshooting the allocation cap by a very large margin.\n        var CAPACITY_DOUBLING_MAX = 1024 * 1024;\n        newCapacity = Math.max(newCapacity, (prevCapacity * (prevCapacity < CAPACITY_DOUBLING_MAX ? 2.0 : 1.125)) >>> 0);\n        if (prevCapacity != 0) newCapacity = Math.max(newCapacity, 256); // At minimum allocate 256b for each file when expanding.\n        var oldContents = node.contents;\n        node.contents = new Uint8Array(newCapacity); // Allocate new storage.\n        if (node.usedBytes > 0) node.contents.set(oldContents.subarray(0, node.usedBytes), 0); // Copy old data over to the new storage.\n      },resizeFileStorage:function(node, newSize) {\n        if (node.usedBytes == newSize) return;\n        if (newSize == 0) {\n          node.contents = null; // Fully decommit when requesting a resize to zero.\n          node.usedBytes = 0;\n        } else {\n          var oldContents = node.contents;\n          node.contents = new Uint8Array(newSize); // Allocate new storage.\n          if (oldContents) {\n            node.contents.set(oldContents.subarray(0, Math.min(newSize, node.usedBytes))); // Copy old data over to the new storage.\n          }\n          node.usedBytes = newSize;\n        }\n      },node_ops:{getattr:function(node) {\n          var attr = {};\n          // device numbers reuse inode numbers.\n          attr.dev = FS.isChrdev(node.mode) ? node.id : 1;\n          attr.ino = node.id;\n          attr.mode = node.mode;\n          attr.nlink = 1;\n          attr.uid = 0;\n          attr.gid = 0;\n          attr.rdev = node.rdev;\n          if (FS.isDir(node.mode)) {\n            attr.size = 4096;\n          } else if (FS.isFile(node.mode)) {\n            attr.size = node.usedBytes;\n          } else if (FS.isLink(node.mode)) {\n            attr.size = node.link.length;\n          } else {\n            attr.size = 0;\n          }\n          attr.atime = new Date(node.timestamp);\n          attr.mtime = new Date(node.timestamp);\n          attr.ctime = new Date(node.timestamp);\n          // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize),\n          //       but this is not required by the standard.\n          attr.blksize = 4096;\n          attr.blocks = Math.ceil(attr.size / attr.blksize);\n          return attr;\n        },setattr:function(node, attr) {\n          if (attr.mode !== undefined) {\n            node.mode = attr.mode;\n          }\n          if (attr.timestamp !== undefined) {\n            node.timestamp = attr.timestamp;\n          }\n          if (attr.size !== undefined) {\n            MEMFS.resizeFileStorage(node, attr.size);\n          }\n        },lookup:function(parent, name) {\n          throw FS.genericErrors[44];\n        },mknod:function(parent, name, mode, dev) {\n          return MEMFS.createNode(parent, name, mode, dev);\n        },rename:function(old_node, new_dir, new_name) {\n          // if we're overwriting a directory at new_name, make sure it's empty.\n          if (FS.isDir(old_node.mode)) {\n            var new_node;\n            try {\n              new_node = FS.lookupNode(new_dir, new_name);\n            } catch (e) {\n            }\n            if (new_node) {\n              for (var i in new_node.contents) {\n                throw new FS.ErrnoError(55);\n              }\n            }\n          }\n          // do the internal rewiring\n          delete old_node.parent.contents[old_node.name];\n          old_node.parent.timestamp = Date.now()\n          old_node.name = new_name;\n          new_dir.contents[new_name] = old_node;\n          new_dir.timestamp = old_node.parent.timestamp;\n          old_node.parent = new_dir;\n        },unlink:function(parent, name) {\n          delete parent.contents[name];\n          parent.timestamp = Date.now();\n        },rmdir:function(parent, name) {\n          var node = FS.lookupNode(parent, name);\n          for (var i in node.contents) {\n            throw new FS.ErrnoError(55);\n          }\n          delete parent.contents[name];\n          parent.timestamp = Date.now();\n        },readdir:function(node) {\n          var entries = ['.', '..'];\n          for (var key in node.contents) {\n            if (!node.contents.hasOwnProperty(key)) {\n              continue;\n            }\n            entries.push(key);\n          }\n          return entries;\n        },symlink:function(parent, newname, oldpath) {\n          var node = MEMFS.createNode(parent, newname, 511 /* 0777 */ | 40960, 0);\n          node.link = oldpath;\n          return node;\n        },readlink:function(node) {\n          if (!FS.isLink(node.mode)) {\n            throw new FS.ErrnoError(28);\n          }\n          return node.link;\n        }},stream_ops:{read:function(stream, buffer, offset, length, position) {\n          var contents = stream.node.contents;\n          if (position >= stream.node.usedBytes) return 0;\n          var size = Math.min(stream.node.usedBytes - position, length);\n          assert(size >= 0);\n          if (size > 8 && contents.subarray) { // non-trivial, and typed array\n            buffer.set(contents.subarray(position, position + size), offset);\n          } else {\n            for (var i = 0; i < size; i++) buffer[offset + i] = contents[position + i];\n          }\n          return size;\n        },write:function(stream, buffer, offset, length, position, canOwn) {\n          // The data buffer should be a typed array view\n          assert(!(buffer instanceof ArrayBuffer));\n  \n          if (!length) return 0;\n          var node = stream.node;\n          node.timestamp = Date.now();\n  \n          if (buffer.subarray && (!node.contents || node.contents.subarray)) { // This write is from a typed array to a typed array?\n            if (canOwn) {\n              assert(position === 0, 'canOwn must imply no weird position inside the file');\n              node.contents = buffer.subarray(offset, offset + length);\n              node.usedBytes = length;\n              return length;\n            } else if (node.usedBytes === 0 && position === 0) { // If this is a simple first write to an empty file, do a fast set since we don't need to care about old data.\n              node.contents = buffer.slice(offset, offset + length);\n              node.usedBytes = length;\n              return length;\n            } else if (position + length <= node.usedBytes) { // Writing to an already allocated and used subrange of the file?\n              node.contents.set(buffer.subarray(offset, offset + length), position);\n              return length;\n            }\n          }\n  \n          // Appending to an existing file and we need to reallocate, or source data did not come as a typed array.\n          MEMFS.expandFileStorage(node, position+length);\n          if (node.contents.subarray && buffer.subarray) {\n            // Use typed array write which is available.\n            node.contents.set(buffer.subarray(offset, offset + length), position);\n          } else {\n            for (var i = 0; i < length; i++) {\n             node.contents[position + i] = buffer[offset + i]; // Or fall back to manual write if not.\n            }\n          }\n          node.usedBytes = Math.max(node.usedBytes, position + length);\n          return length;\n        },llseek:function(stream, offset, whence) {\n          var position = offset;\n          if (whence === 1) {\n            position += stream.position;\n          } else if (whence === 2) {\n            if (FS.isFile(stream.node.mode)) {\n              position += stream.node.usedBytes;\n            }\n          }\n          if (position < 0) {\n            throw new FS.ErrnoError(28);\n          }\n          return position;\n        },allocate:function(stream, offset, length) {\n          MEMFS.expandFileStorage(stream.node, offset + length);\n          stream.node.usedBytes = Math.max(stream.node.usedBytes, offset + length);\n        },mmap:function(stream, address, length, position, prot, flags) {\n          if (address !== 0) {\n            // We don't currently support location hints for the address of the mapping\n            throw new FS.ErrnoError(28);\n          }\n          if (!FS.isFile(stream.node.mode)) {\n            throw new FS.ErrnoError(43);\n          }\n          var ptr;\n          var allocated;\n          var contents = stream.node.contents;\n          // Only make a new copy when MAP_PRIVATE is specified.\n          if (!(flags & 2) && contents.buffer === buffer) {\n            // We can't emulate MAP_SHARED when the file is not backed by the buffer\n            // we're mapping to (e.g. the HEAP buffer).\n            allocated = false;\n            ptr = contents.byteOffset;\n          } else {\n            // Try to avoid unnecessary slices.\n            if (position > 0 || position + length < contents.length) {\n              if (contents.subarray) {\n                contents = contents.subarray(position, position + length);\n              } else {\n                contents = Array.prototype.slice.call(contents, position, position + length);\n              }\n            }\n            allocated = true;\n            ptr = mmapAlloc(length);\n            if (!ptr) {\n              throw new FS.ErrnoError(48);\n            }\n            HEAP8.set(contents, ptr);\n          }\n          return { ptr: ptr, allocated: allocated };\n        },msync:function(stream, buffer, offset, length, mmapFlags) {\n          if (!FS.isFile(stream.node.mode)) {\n            throw new FS.ErrnoError(43);\n          }\n          if (mmapFlags & 2) {\n            // MAP_PRIVATE calls need not to be synced back to underlying fs\n            return 0;\n          }\n  \n          var bytesWritten = MEMFS.stream_ops.write(stream, buffer, 0, length, offset, false);\n          // should we check if bytesWritten and length are the same?\n          return 0;\n        }}};\n  \n  var ERRNO_MESSAGES={0:\"Success\",1:\"Arg list too long\",2:\"Permission denied\",3:\"Address already in use\",4:\"Address not available\",5:\"Address family not supported by protocol family\",6:\"No more processes\",7:\"Socket already connected\",8:\"Bad file number\",9:\"Trying to read unreadable message\",10:\"Mount device busy\",11:\"Operation canceled\",12:\"No children\",13:\"Connection aborted\",14:\"Connection refused\",15:\"Connection reset by peer\",16:\"File locking deadlock error\",17:\"Destination address required\",18:\"Math arg out of domain of func\",19:\"Quota exceeded\",20:\"File exists\",21:\"Bad address\",22:\"File too large\",23:\"Host is unreachable\",24:\"Identifier removed\",25:\"Illegal byte sequence\",26:\"Connection already in progress\",27:\"Interrupted system call\",28:\"Invalid argument\",29:\"I/O error\",30:\"Socket is already connected\",31:\"Is a directory\",32:\"Too many symbolic links\",33:\"Too many open files\",34:\"Too many links\",35:\"Message too long\",36:\"Multihop attempted\",37:\"File or path name too long\",38:\"Network interface is not configured\",39:\"Connection reset by network\",40:\"Network is unreachable\",41:\"Too many open files in system\",42:\"No buffer space available\",43:\"No such device\",44:\"No such file or directory\",45:\"Exec format error\",46:\"No record locks available\",47:\"The link has been severed\",48:\"Not enough core\",49:\"No message of desired type\",50:\"Protocol not available\",51:\"No space left on device\",52:\"Function not implemented\",53:\"Socket is not connected\",54:\"Not a directory\",55:\"Directory not empty\",56:\"State not recoverable\",57:\"Socket operation on non-socket\",59:\"Not a typewriter\",60:\"No such device or address\",61:\"Value too large for defined data type\",62:\"Previous owner died\",63:\"Not super-user\",64:\"Broken pipe\",65:\"Protocol error\",66:\"Unknown protocol\",67:\"Protocol wrong type for socket\",68:\"Math result not representable\",69:\"Read only file system\",70:\"Illegal seek\",71:\"No such process\",72:\"Stale file handle\",73:\"Connection timed out\",74:\"Text file busy\",75:\"Cross-device link\",100:\"Device not a stream\",101:\"Bad font file fmt\",102:\"Invalid slot\",103:\"Invalid request code\",104:\"No anode\",105:\"Block device required\",106:\"Channel number out of range\",107:\"Level 3 halted\",108:\"Level 3 reset\",109:\"Link number out of range\",110:\"Protocol driver not attached\",111:\"No CSI structure available\",112:\"Level 2 halted\",113:\"Invalid exchange\",114:\"Invalid request descriptor\",115:\"Exchange full\",116:\"No data (for no delay io)\",117:\"Timer expired\",118:\"Out of streams resources\",119:\"Machine is not on the network\",120:\"Package not installed\",121:\"The object is remote\",122:\"Advertise error\",123:\"Srmount error\",124:\"Communication error on send\",125:\"Cross mount point (not really error)\",126:\"Given log. name not unique\",127:\"f.d. invalid for this operation\",128:\"Remote address changed\",129:\"Can   access a needed shared lib\",130:\"Accessing a corrupted shared lib\",131:\".lib section in a.out corrupted\",132:\"Attempting to link in too many libs\",133:\"Attempting to exec a shared library\",135:\"Streams pipe error\",136:\"Too many users\",137:\"Socket type not supported\",138:\"Not supported\",139:\"Protocol family not supported\",140:\"Can't send after socket shutdown\",141:\"Too many references\",142:\"Host is down\",148:\"No medium (in tape drive)\",156:\"Level 2 not synchronized\"};\n  \n  var ERRNO_CODES={EPERM:63,ENOENT:44,ESRCH:71,EINTR:27,EIO:29,ENXIO:60,E2BIG:1,ENOEXEC:45,EBADF:8,ECHILD:12,EAGAIN:6,EWOULDBLOCK:6,ENOMEM:48,EACCES:2,EFAULT:21,ENOTBLK:105,EBUSY:10,EEXIST:20,EXDEV:75,ENODEV:43,ENOTDIR:54,EISDIR:31,EINVAL:28,ENFILE:41,EMFILE:33,ENOTTY:59,ETXTBSY:74,EFBIG:22,ENOSPC:51,ESPIPE:70,EROFS:69,EMLINK:34,EPIPE:64,EDOM:18,ERANGE:68,ENOMSG:49,EIDRM:24,ECHRNG:106,EL2NSYNC:156,EL3HLT:107,EL3RST:108,ELNRNG:109,EUNATCH:110,ENOCSI:111,EL2HLT:112,EDEADLK:16,ENOLCK:46,EBADE:113,EBADR:114,EXFULL:115,ENOANO:104,EBADRQC:103,EBADSLT:102,EDEADLOCK:16,EBFONT:101,ENOSTR:100,ENODATA:116,ETIME:117,ENOSR:118,ENONET:119,ENOPKG:120,EREMOTE:121,ENOLINK:47,EADV:122,ESRMNT:123,ECOMM:124,EPROTO:65,EMULTIHOP:36,EDOTDOT:125,EBADMSG:9,ENOTUNIQ:126,EBADFD:127,EREMCHG:128,ELIBACC:129,ELIBBAD:130,ELIBSCN:131,ELIBMAX:132,ELIBEXEC:133,ENOSYS:52,ENOTEMPTY:55,ENAMETOOLONG:37,ELOOP:32,EOPNOTSUPP:138,EPFNOSUPPORT:139,ECONNRESET:15,ENOBUFS:42,EAFNOSUPPORT:5,EPROTOTYPE:67,ENOTSOCK:57,ENOPROTOOPT:50,ESHUTDOWN:140,ECONNREFUSED:14,EADDRINUSE:3,ECONNABORTED:13,ENETUNREACH:40,ENETDOWN:38,ETIMEDOUT:73,EHOSTDOWN:142,EHOSTUNREACH:23,EINPROGRESS:26,EALREADY:7,EDESTADDRREQ:17,EMSGSIZE:35,EPROTONOSUPPORT:66,ESOCKTNOSUPPORT:137,EADDRNOTAVAIL:4,ENETRESET:39,EISCONN:30,ENOTCONN:53,ETOOMANYREFS:141,EUSERS:136,EDQUOT:19,ESTALE:72,ENOTSUP:138,ENOMEDIUM:148,EILSEQ:25,EOVERFLOW:61,ECANCELED:11,ENOTRECOVERABLE:56,EOWNERDEAD:62,ESTRPIPE:135};\n  var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:\"/\",initialized:false,ignorePermissions:true,trackingDelegate:{},tracking:{openFlags:{READ:1,WRITE:2}},ErrnoError:null,genericErrors:{},filesystems:null,syncFSRequests:0,lookupPath:function(path, opts) {\n        path = PATH_FS.resolve(FS.cwd(), path);\n        opts = opts || {};\n  \n        if (!path) return { path: '', node: null };\n  \n        var defaults = {\n          follow_mount: true,\n          recurse_count: 0\n        };\n        for (var key in defaults) {\n          if (opts[key] === undefined) {\n            opts[key] = defaults[key];\n          }\n        }\n  \n        if (opts.recurse_count > 8) {  // max recursive lookup of 8\n          throw new FS.ErrnoError(32);\n        }\n  \n        // split the path\n        var parts = PATH.normalizeArray(path.split('/').filter(function(p) {\n          return !!p;\n        }), false);\n  \n        // start at the root\n        var current = FS.root;\n        var current_path = '/';\n  \n        for (var i = 0; i < parts.length; i++) {\n          var islast = (i === parts.length-1);\n          if (islast && opts.parent) {\n            // stop resolving\n            break;\n          }\n  \n          current = FS.lookupNode(current, parts[i]);\n          current_path = PATH.join2(current_path, parts[i]);\n  \n          // jump to the mount's root node if this is a mountpoint\n          if (FS.isMountpoint(current)) {\n            if (!islast || (islast && opts.follow_mount)) {\n              current = current.mounted.root;\n            }\n          }\n  \n          // by default, lookupPath will not follow a symlink if it is the final path component.\n          // setting opts.follow = true will override this behavior.\n          if (!islast || opts.follow) {\n            var count = 0;\n            while (FS.isLink(current.mode)) {\n              var link = FS.readlink(current_path);\n              current_path = PATH_FS.resolve(PATH.dirname(current_path), link);\n  \n              var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count });\n              current = lookup.node;\n  \n              if (count++ > 40) {  // limit max consecutive symlinks to 40 (SYMLOOP_MAX).\n                throw new FS.ErrnoError(32);\n              }\n            }\n          }\n        }\n  \n        return { path: current_path, node: current };\n      },getPath:function(node) {\n        var path;\n        while (true) {\n          if (FS.isRoot(node)) {\n            var mount = node.mount.mountpoint;\n            if (!path) return mount;\n            return mount[mount.length-1] !== '/' ? mount + '/' + path : mount + path;\n          }\n          path = path ? node.name + '/' + path : node.name;\n          node = node.parent;\n        }\n      },hashName:function(parentid, name) {\n        var hash = 0;\n  \n        for (var i = 0; i < name.length; i++) {\n          hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0;\n        }\n        return ((parentid + hash) >>> 0) % FS.nameTable.length;\n      },hashAddNode:function(node) {\n        var hash = FS.hashName(node.parent.id, node.name);\n        node.name_next = FS.nameTable[hash];\n        FS.nameTable[hash] = node;\n      },hashRemoveNode:function(node) {\n        var hash = FS.hashName(node.parent.id, node.name);\n        if (FS.nameTable[hash] === node) {\n          FS.nameTable[hash] = node.name_next;\n        } else {\n          var current = FS.nameTable[hash];\n          while (current) {\n            if (current.name_next === node) {\n              current.name_next = node.name_next;\n              break;\n            }\n            current = current.name_next;\n          }\n        }\n      },lookupNode:function(parent, name) {\n        var errCode = FS.mayLookup(parent);\n        if (errCode) {\n          throw new FS.ErrnoError(errCode, parent);\n        }\n        var hash = FS.hashName(parent.id, name);\n        for (var node = FS.nameTable[hash]; node; node = node.name_next) {\n          var nodeName = node.name;\n          if (node.parent.id === parent.id && nodeName === name) {\n            return node;\n          }\n        }\n        // if we failed to find it in the cache, call into the VFS\n        return FS.lookup(parent, name);\n      },createNode:function(parent, name, mode, rdev) {\n        assert(typeof parent === 'object')\n        var node = new FS.FSNode(parent, name, mode, rdev);\n  \n        FS.hashAddNode(node);\n  \n        return node;\n      },destroyNode:function(node) {\n        FS.hashRemoveNode(node);\n      },isRoot:function(node) {\n        return node === node.parent;\n      },isMountpoint:function(node) {\n        return !!node.mounted;\n      },isFile:function(mode) {\n        return (mode & 61440) === 32768;\n      },isDir:function(mode) {\n        return (mode & 61440) === 16384;\n      },isLink:function(mode) {\n        return (mode & 61440) === 40960;\n      },isChrdev:function(mode) {\n        return (mode & 61440) === 8192;\n      },isBlkdev:function(mode) {\n        return (mode & 61440) === 24576;\n      },isFIFO:function(mode) {\n        return (mode & 61440) === 4096;\n      },isSocket:function(mode) {\n        return (mode & 49152) === 49152;\n      },flagModes:{\"r\":0,\"r+\":2,\"w\":577,\"w+\":578,\"a\":1089,\"a+\":1090},modeStringToFlags:function(str) {\n        var flags = FS.flagModes[str];\n        if (typeof flags === 'undefined') {\n          throw new Error('Unknown file open mode: ' + str);\n        }\n        return flags;\n      },flagsToPermissionString:function(flag) {\n        var perms = ['r', 'w', 'rw'][flag & 3];\n        if ((flag & 512)) {\n          perms += 'w';\n        }\n        return perms;\n      },nodePermissions:function(node, perms) {\n        if (FS.ignorePermissions) {\n          return 0;\n        }\n        // return 0 if any user, group or owner bits are set.\n        if (perms.indexOf('r') !== -1 && !(node.mode & 292)) {\n          return 2;\n        } else if (perms.indexOf('w') !== -1 && !(node.mode & 146)) {\n          return 2;\n        } else if (perms.indexOf('x') !== -1 && !(node.mode & 73)) {\n          return 2;\n        }\n        return 0;\n      },mayLookup:function(dir) {\n        var errCode = FS.nodePermissions(dir, 'x');\n        if (errCode) return errCode;\n        if (!dir.node_ops.lookup) return 2;\n        return 0;\n      },mayCreate:function(dir, name) {\n        try {\n          var node = FS.lookupNode(dir, name);\n          return 20;\n        } catch (e) {\n        }\n        return FS.nodePermissions(dir, 'wx');\n      },mayDelete:function(dir, name, isdir) {\n        var node;\n        try {\n          node = FS.lookupNode(dir, name);\n        } catch (e) {\n          return e.errno;\n        }\n        var errCode = FS.nodePermissions(dir, 'wx');\n        if (errCode) {\n          return errCode;\n        }\n        if (isdir) {\n          if (!FS.isDir(node.mode)) {\n            return 54;\n          }\n          if (FS.isRoot(node) || FS.getPath(node) === FS.cwd()) {\n            return 10;\n          }\n        } else {\n          if (FS.isDir(node.mode)) {\n            return 31;\n          }\n        }\n        return 0;\n      },mayOpen:function(node, flags) {\n        if (!node) {\n          return 44;\n        }\n        if (FS.isLink(node.mode)) {\n          return 32;\n        } else if (FS.isDir(node.mode)) {\n          if (FS.flagsToPermissionString(flags) !== 'r' || // opening for write\n              (flags & 512)) { // TODO: check for O_SEARCH? (== search for dir only)\n            return 31;\n          }\n        }\n        return FS.nodePermissions(node, FS.flagsToPermissionString(flags));\n      },MAX_OPEN_FDS:4096,nextfd:function(fd_start, fd_end) {\n        fd_start = fd_start || 0;\n        fd_end = fd_end || FS.MAX_OPEN_FDS;\n        for (var fd = fd_start; fd <= fd_end; fd++) {\n          if (!FS.streams[fd]) {\n            return fd;\n          }\n        }\n        throw new FS.ErrnoError(33);\n      },getStream:function(fd) {\n        return FS.streams[fd];\n      },createStream:function(stream, fd_start, fd_end) {\n        if (!FS.FSStream) {\n          FS.FSStream = /** @constructor */ function(){};\n          FS.FSStream.prototype = {\n            object: {\n              get: function() { return this.node; },\n              set: function(val) { this.node = val; }\n            },\n            isRead: {\n              get: function() { return (this.flags & 2097155) !== 1; }\n            },\n            isWrite: {\n              get: function() { return (this.flags & 2097155) !== 0; }\n            },\n            isAppend: {\n              get: function() { return (this.flags & 1024); }\n            }\n          };\n        }\n        // clone it, so we can return an instance of FSStream\n        var newStream = new FS.FSStream();\n        for (var p in stream) {\n          newStream[p] = stream[p];\n        }\n        stream = newStream;\n        var fd = FS.nextfd(fd_start, fd_end);\n        stream.fd = fd;\n        FS.streams[fd] = stream;\n        return stream;\n      },closeStream:function(fd) {\n        FS.streams[fd] = null;\n      },chrdev_stream_ops:{open:function(stream) {\n          var device = FS.getDevice(stream.node.rdev);\n          // override node's stream ops with the device's\n          stream.stream_ops = device.stream_ops;\n          // forward the open call\n          if (stream.stream_ops.open) {\n            stream.stream_ops.open(stream);\n          }\n        },llseek:function() {\n          throw new FS.ErrnoError(70);\n        }},major:function(dev) {\n        return ((dev) >> 8);\n      },minor:function(dev) {\n        return ((dev) & 0xff);\n      },makedev:function(ma, mi) {\n        return ((ma) << 8 | (mi));\n      },registerDevice:function(dev, ops) {\n        FS.devices[dev] = { stream_ops: ops };\n      },getDevice:function(dev) {\n        return FS.devices[dev];\n      },getMounts:function(mount) {\n        var mounts = [];\n        var check = [mount];\n  \n        while (check.length) {\n          var m = check.pop();\n  \n          mounts.push(m);\n  \n          check.push.apply(check, m.mounts);\n        }\n  \n        return mounts;\n      },syncfs:function(populate, callback) {\n        if (typeof(populate) === 'function') {\n          callback = populate;\n          populate = false;\n        }\n  \n        FS.syncFSRequests++;\n  \n        if (FS.syncFSRequests > 1) {\n          err('warning: ' + FS.syncFSRequests + ' FS.syncfs operations in flight at once, probably just doing extra work');\n        }\n  \n        var mounts = FS.getMounts(FS.root.mount);\n        var completed = 0;\n  \n        function doCallback(errCode) {\n          assert(FS.syncFSRequests > 0);\n          FS.syncFSRequests--;\n          return callback(errCode);\n        }\n  \n        function done(errCode) {\n          if (errCode) {\n            if (!done.errored) {\n              done.errored = true;\n              return doCallback(errCode);\n            }\n            return;\n          }\n          if (++completed >= mounts.length) {\n            doCallback(null);\n          }\n        };\n  \n        // sync all mounts\n        mounts.forEach(function (mount) {\n          if (!mount.type.syncfs) {\n            return done(null);\n          }\n          mount.type.syncfs(mount, populate, done);\n        });\n      },mount:function(type, opts, mountpoint) {\n        if (typeof type === 'string') {\n          // The filesystem was not included, and instead we have an error\n          // message stored in the variable.\n          throw type;\n        }\n        var root = mountpoint === '/';\n        var pseudo = !mountpoint;\n        var node;\n  \n        if (root && FS.root) {\n          throw new FS.ErrnoError(10);\n        } else if (!root && !pseudo) {\n          var lookup = FS.lookupPath(mountpoint, { follow_mount: false });\n  \n          mountpoint = lookup.path;  // use the absolute path\n          node = lookup.node;\n  \n          if (FS.isMountpoint(node)) {\n            throw new FS.ErrnoError(10);\n          }\n  \n          if (!FS.isDir(node.mode)) {\n            throw new FS.ErrnoError(54);\n          }\n        }\n  \n        var mount = {\n          type: type,\n          opts: opts,\n          mountpoint: mountpoint,\n          mounts: []\n        };\n  \n        // create a root node for the fs\n        var mountRoot = type.mount(mount);\n        mountRoot.mount = mount;\n        mount.root = mountRoot;\n  \n        if (root) {\n          FS.root = mountRoot;\n        } else if (node) {\n          // set as a mountpoint\n          node.mounted = mount;\n  \n          // add the new mount to the current mount's children\n          if (node.mount) {\n            node.mount.mounts.push(mount);\n          }\n        }\n  \n        return mountRoot;\n      },unmount:function (mountpoint) {\n        var lookup = FS.lookupPath(mountpoint, { follow_mount: false });\n  \n        if (!FS.isMountpoint(lookup.node)) {\n          throw new FS.ErrnoError(28);\n        }\n  \n        // destroy the nodes for this mount, and all its child mounts\n        var node = lookup.node;\n        var mount = node.mounted;\n        var mounts = FS.getMounts(mount);\n  \n        Object.keys(FS.nameTable).forEach(function (hash) {\n          var current = FS.nameTable[hash];\n  \n          while (current) {\n            var next = current.name_next;\n  \n            if (mounts.indexOf(current.mount) !== -1) {\n              FS.destroyNode(current);\n            }\n  \n            current = next;\n          }\n        });\n  \n        // no longer a mountpoint\n        node.mounted = null;\n  \n        // remove this mount from the child mounts\n        var idx = node.mount.mounts.indexOf(mount);\n        assert(idx !== -1);\n        node.mount.mounts.splice(idx, 1);\n      },lookup:function(parent, name) {\n        return parent.node_ops.lookup(parent, name);\n      },mknod:function(path, mode, dev) {\n        var lookup = FS.lookupPath(path, { parent: true });\n        var parent = lookup.node;\n        var name = PATH.basename(path);\n        if (!name || name === '.' || name === '..') {\n          throw new FS.ErrnoError(28);\n        }\n        var errCode = FS.mayCreate(parent, name);\n        if (errCode) {\n          throw new FS.ErrnoError(errCode);\n        }\n        if (!parent.node_ops.mknod) {\n          throw new FS.ErrnoError(63);\n        }\n        return parent.node_ops.mknod(parent, name, mode, dev);\n      },create:function(path, mode) {\n        mode = mode !== undefined ? mode : 438 /* 0666 */;\n        mode &= 4095;\n        mode |= 32768;\n        return FS.mknod(path, mode, 0);\n      },mkdir:function(path, mode) {\n        mode = mode !== undefined ? mode : 511 /* 0777 */;\n        mode &= 511 | 512;\n        mode |= 16384;\n        return FS.mknod(path, mode, 0);\n      },mkdirTree:function(path, mode) {\n        var dirs = path.split('/');\n        var d = '';\n        for (var i = 0; i < dirs.length; ++i) {\n          if (!dirs[i]) continue;\n          d += '/' + dirs[i];\n          try {\n            FS.mkdir(d, mode);\n          } catch(e) {\n            if (e.errno != 20) throw e;\n          }\n        }\n      },mkdev:function(path, mode, dev) {\n        if (typeof(dev) === 'undefined') {\n          dev = mode;\n          mode = 438 /* 0666 */;\n        }\n        mode |= 8192;\n        return FS.mknod(path, mode, dev);\n      },symlink:function(oldpath, newpath) {\n        if (!PATH_FS.resolve(oldpath)) {\n          throw new FS.ErrnoError(44);\n        }\n        var lookup = FS.lookupPath(newpath, { parent: true });\n        var parent = lookup.node;\n        if (!parent) {\n          throw new FS.ErrnoError(44);\n        }\n        var newname = PATH.basename(newpath);\n        var errCode = FS.mayCreate(parent, newname);\n        if (errCode) {\n          throw new FS.ErrnoError(errCode);\n        }\n        if (!parent.node_ops.symlink) {\n          throw new FS.ErrnoError(63);\n        }\n        return parent.node_ops.symlink(parent, newname, oldpath);\n      },rename:function(old_path, new_path) {\n        var old_dirname = PATH.dirname(old_path);\n        var new_dirname = PATH.dirname(new_path);\n        var old_name = PATH.basename(old_path);\n        var new_name = PATH.basename(new_path);\n        // parents must exist\n        var lookup, old_dir, new_dir;\n  \n        // let the errors from non existant directories percolate up\n        lookup = FS.lookupPath(old_path, { parent: true });\n        old_dir = lookup.node;\n        lookup = FS.lookupPath(new_path, { parent: true });\n        new_dir = lookup.node;\n  \n        if (!old_dir || !new_dir) throw new FS.ErrnoError(44);\n        // need to be part of the same mount\n        if (old_dir.mount !== new_dir.mount) {\n          throw new FS.ErrnoError(75);\n        }\n        // source must exist\n        var old_node = FS.lookupNode(old_dir, old_name);\n        // old path should not be an ancestor of the new path\n        var relative = PATH_FS.relative(old_path, new_dirname);\n        if (relative.charAt(0) !== '.') {\n          throw new FS.ErrnoError(28);\n        }\n        // new path should not be an ancestor of the old path\n        relative = PATH_FS.relative(new_path, old_dirname);\n        if (relative.charAt(0) !== '.') {\n          throw new FS.ErrnoError(55);\n        }\n        // see if the new path already exists\n        var new_node;\n        try {\n          new_node = FS.lookupNode(new_dir, new_name);\n        } catch (e) {\n          // not fatal\n        }\n        // early out if nothing needs to change\n        if (old_node === new_node) {\n          return;\n        }\n        // we'll need to delete the old entry\n        var isdir = FS.isDir(old_node.mode);\n        var errCode = FS.mayDelete(old_dir, old_name, isdir);\n        if (errCode) {\n          throw new FS.ErrnoError(errCode);\n        }\n        // need delete permissions if we'll be overwriting.\n        // need create permissions if new doesn't already exist.\n        errCode = new_node ?\n          FS.mayDelete(new_dir, new_name, isdir) :\n          FS.mayCreate(new_dir, new_name);\n        if (errCode) {\n          throw new FS.ErrnoError(errCode);\n        }\n        if (!old_dir.node_ops.rename) {\n          throw new FS.ErrnoError(63);\n        }\n        if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) {\n          throw new FS.ErrnoError(10);\n        }\n        // if we are going to change the parent, check write permissions\n        if (new_dir !== old_dir) {\n          errCode = FS.nodePermissions(old_dir, 'w');\n          if (errCode) {\n            throw new FS.ErrnoError(errCode);\n          }\n        }\n        try {\n          if (FS.trackingDelegate['willMovePath']) {\n            FS.trackingDelegate['willMovePath'](old_path, new_path);\n          }\n        } catch(e) {\n          err(\"FS.trackingDelegate['willMovePath']('\"+old_path+\"', '\"+new_path+\"') threw an exception: \" + e.message);\n        }\n        // remove the node from the lookup hash\n        FS.hashRemoveNode(old_node);\n        // do the underlying fs rename\n        try {\n          old_dir.node_ops.rename(old_node, new_dir, new_name);\n        } catch (e) {\n          throw e;\n        } finally {\n          // add the node back to the hash (in case node_ops.rename\n          // changed its name)\n          FS.hashAddNode(old_node);\n        }\n        try {\n          if (FS.trackingDelegate['onMovePath']) FS.trackingDelegate['onMovePath'](old_path, new_path);\n        } catch(e) {\n          err(\"FS.trackingDelegate['onMovePath']('\"+old_path+\"', '\"+new_path+\"') threw an exception: \" + e.message);\n        }\n      },rmdir:function(path) {\n        var lookup = FS.lookupPath(path, { parent: true });\n        var parent = lookup.node;\n        var name = PATH.basename(path);\n        var node = FS.lookupNode(parent, name);\n        var errCode = FS.mayDelete(parent, name, true);\n        if (errCode) {\n          throw new FS.ErrnoError(errCode);\n        }\n        if (!parent.node_ops.rmdir) {\n          throw new FS.ErrnoError(63);\n        }\n        if (FS.isMountpoint(node)) {\n          throw new FS.ErrnoError(10);\n        }\n        try {\n          if (FS.trackingDelegate['willDeletePath']) {\n            FS.trackingDelegate['willDeletePath'](path);\n          }\n        } catch(e) {\n          err(\"FS.trackingDelegate['willDeletePath']('\"+path+\"') threw an exception: \" + e.message);\n        }\n        parent.node_ops.rmdir(parent, name);\n        FS.destroyNode(node);\n        try {\n          if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path);\n        } catch(e) {\n          err(\"FS.trackingDelegate['onDeletePath']('\"+path+\"') threw an exception: \" + e.message);\n        }\n      },readdir:function(path) {\n        var lookup = FS.lookupPath(path, { follow: true });\n        var node = lookup.node;\n        if (!node.node_ops.readdir) {\n          throw new FS.ErrnoError(54);\n        }\n        return node.node_ops.readdir(node);\n      },unlink:function(path) {\n        var lookup = FS.lookupPath(path, { parent: true });\n        var parent = lookup.node;\n        var name = PATH.basename(path);\n        var node = FS.lookupNode(parent, name);\n        var errCode = FS.mayDelete(parent, name, false);\n        if (errCode) {\n          // According to POSIX, we should map EISDIR to EPERM, but\n          // we instead do what Linux does (and we must, as we use\n          // the musl linux libc).\n          throw new FS.ErrnoError(errCode);\n        }\n        if (!parent.node_ops.unlink) {\n          throw new FS.ErrnoError(63);\n        }\n        if (FS.isMountpoint(node)) {\n          throw new FS.ErrnoError(10);\n        }\n        try {\n          if (FS.trackingDelegate['willDeletePath']) {\n            FS.trackingDelegate['willDeletePath'](path);\n          }\n        } catch(e) {\n          err(\"FS.trackingDelegate['willDeletePath']('\"+path+\"') threw an exception: \" + e.message);\n        }\n        parent.node_ops.unlink(parent, name);\n        FS.destroyNode(node);\n        try {\n          if (FS.trackingDelegate['onDeletePath']) FS.trackingDelegate['onDeletePath'](path);\n        } catch(e) {\n          err(\"FS.trackingDelegate['onDeletePath']('\"+path+\"') threw an exception: \" + e.message);\n        }\n      },readlink:function(path) {\n        var lookup = FS.lookupPath(path);\n        var link = lookup.node;\n        if (!link) {\n          throw new FS.ErrnoError(44);\n        }\n        if (!link.node_ops.readlink) {\n          throw new FS.ErrnoError(28);\n        }\n        return PATH_FS.resolve(FS.getPath(link.parent), link.node_ops.readlink(link));\n      },stat:function(path, dontFollow) {\n        var lookup = FS.lookupPath(path, { follow: !dontFollow });\n        var node = lookup.node;\n        if (!node) {\n          throw new FS.ErrnoError(44);\n        }\n        if (!node.node_ops.getattr) {\n          throw new FS.ErrnoError(63);\n        }\n        return node.node_ops.getattr(node);\n      },lstat:function(path) {\n        return FS.stat(path, true);\n      },chmod:function(path, mode, dontFollow) {\n        var node;\n        if (typeof path === 'string') {\n          var lookup = FS.lookupPath(path, { follow: !dontFollow });\n          node = lookup.node;\n        } else {\n          node = path;\n        }\n        if (!node.node_ops.setattr) {\n          throw new FS.ErrnoError(63);\n        }\n        node.node_ops.setattr(node, {\n          mode: (mode & 4095) | (node.mode & ~4095),\n          timestamp: Date.now()\n        });\n      },lchmod:function(path, mode) {\n        FS.chmod(path, mode, true);\n      },fchmod:function(fd, mode) {\n        var stream = FS.getStream(fd);\n        if (!stream) {\n          throw new FS.ErrnoError(8);\n        }\n        FS.chmod(stream.node, mode);\n      },chown:function(path, uid, gid, dontFollow) {\n        var node;\n        if (typeof path === 'string') {\n          var lookup = FS.lookupPath(path, { follow: !dontFollow });\n          node = lookup.node;\n        } else {\n          node = path;\n        }\n        if (!node.node_ops.setattr) {\n          throw new FS.ErrnoError(63);\n        }\n        node.node_ops.setattr(node, {\n          timestamp: Date.now()\n          // we ignore the uid / gid for now\n        });\n      },lchown:function(path, uid, gid) {\n        FS.chown(path, uid, gid, true);\n      },fchown:function(fd, uid, gid) {\n        var stream = FS.getStream(fd);\n        if (!stream) {\n          throw new FS.ErrnoError(8);\n        }\n        FS.chown(stream.node, uid, gid);\n      },truncate:function(path, len) {\n        if (len < 0) {\n          throw new FS.ErrnoError(28);\n        }\n        var node;\n        if (typeof path === 'string') {\n          var lookup = FS.lookupPath(path, { follow: true });\n          node = lookup.node;\n        } else {\n          node = path;\n        }\n        if (!node.node_ops.setattr) {\n          throw new FS.ErrnoError(63);\n        }\n        if (FS.isDir(node.mode)) {\n          throw new FS.ErrnoError(31);\n        }\n        if (!FS.isFile(node.mode)) {\n          throw new FS.ErrnoError(28);\n        }\n        var errCode = FS.nodePermissions(node, 'w');\n        if (errCode) {\n          throw new FS.ErrnoError(errCode);\n        }\n        node.node_ops.setattr(node, {\n          size: len,\n          timestamp: Date.now()\n        });\n      },ftruncate:function(fd, len) {\n        var stream = FS.getStream(fd);\n        if (!stream) {\n          throw new FS.ErrnoError(8);\n        }\n        if ((stream.flags & 2097155) === 0) {\n          throw new FS.ErrnoError(28);\n        }\n        FS.truncate(stream.node, len);\n      },utime:function(path, atime, mtime) {\n        var lookup = FS.lookupPath(path, { follow: true });\n        var node = lookup.node;\n        node.node_ops.setattr(node, {\n          timestamp: Math.max(atime, mtime)\n        });\n      },open:function(path, flags, mode, fd_start, fd_end) {\n        if (path === \"\") {\n          throw new FS.ErrnoError(44);\n        }\n        flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags;\n        mode = typeof mode === 'undefined' ? 438 /* 0666 */ : mode;\n        if ((flags & 64)) {\n          mode = (mode & 4095) | 32768;\n        } else {\n          mode = 0;\n        }\n        var node;\n        if (typeof path === 'object') {\n          node = path;\n        } else {\n          path = PATH.normalize(path);\n          try {\n            var lookup = FS.lookupPath(path, {\n              follow: !(flags & 131072)\n            });\n            node = lookup.node;\n          } catch (e) {\n            // ignore\n          }\n        }\n        // perhaps we need to create the node\n        var created = false;\n        if ((flags & 64)) {\n          if (node) {\n            // if O_CREAT and O_EXCL are set, error out if the node already exists\n            if ((flags & 128)) {\n              throw new FS.ErrnoError(20);\n            }\n          } else {\n            // node doesn't exist, try to create it\n            node = FS.mknod(path, mode, 0);\n            created = true;\n          }\n        }\n        if (!node) {\n          throw new FS.ErrnoError(44);\n        }\n        // can't truncate a device\n        if (FS.isChrdev(node.mode)) {\n          flags &= ~512;\n        }\n        // if asked only for a directory, then this must be one\n        if ((flags & 65536) && !FS.isDir(node.mode)) {\n          throw new FS.ErrnoError(54);\n        }\n        // check permissions, if this is not a file we just created now (it is ok to\n        // create and write to a file with read-only permissions; it is read-only\n        // for later use)\n        if (!created) {\n          var errCode = FS.mayOpen(node, flags);\n          if (errCode) {\n            throw new FS.ErrnoError(errCode);\n          }\n        }\n        // do truncation if necessary\n        if ((flags & 512)) {\n          FS.truncate(node, 0);\n        }\n        // we've already handled these, don't pass down to the underlying vfs\n        flags &= ~(128 | 512 | 131072);\n  \n        // register the stream with the filesystem\n        var stream = FS.createStream({\n          node: node,\n          path: FS.getPath(node),  // we want the absolute path to the node\n          flags: flags,\n          seekable: true,\n          position: 0,\n          stream_ops: node.stream_ops,\n          // used by the file family libc calls (fopen, fwrite, ferror, etc.)\n          ungotten: [],\n          error: false\n        }, fd_start, fd_end);\n        // call the new stream's open function\n        if (stream.stream_ops.open) {\n          stream.stream_ops.open(stream);\n        }\n        if (Module['logReadFiles'] && !(flags & 1)) {\n          if (!FS.readFiles) FS.readFiles = {};\n          if (!(path in FS.readFiles)) {\n            FS.readFiles[path] = 1;\n            err(\"FS.trackingDelegate error on read file: \" + path);\n          }\n        }\n        try {\n          if (FS.trackingDelegate['onOpenFile']) {\n            var trackingFlags = 0;\n            if ((flags & 2097155) !== 1) {\n              trackingFlags |= FS.tracking.openFlags.READ;\n            }\n            if ((flags & 2097155) !== 0) {\n              trackingFlags |= FS.tracking.openFlags.WRITE;\n            }\n            FS.trackingDelegate['onOpenFile'](path, trackingFlags);\n          }\n        } catch(e) {\n          err(\"FS.trackingDelegate['onOpenFile']('\"+path+\"', flags) threw an exception: \" + e.message);\n        }\n        return stream;\n      },close:function(stream) {\n        if (FS.isClosed(stream)) {\n          throw new FS.ErrnoError(8);\n        }\n        if (stream.getdents) stream.getdents = null; // free readdir state\n        try {\n          if (stream.stream_ops.close) {\n            stream.stream_ops.close(stream);\n          }\n        } catch (e) {\n          throw e;\n        } finally {\n          FS.closeStream(stream.fd);\n        }\n        stream.fd = null;\n      },isClosed:function(stream) {\n        return stream.fd === null;\n      },llseek:function(stream, offset, whence) {\n        if (FS.isClosed(stream)) {\n          throw new FS.ErrnoError(8);\n        }\n        if (!stream.seekable || !stream.stream_ops.llseek) {\n          throw new FS.ErrnoError(70);\n        }\n        if (whence != 0 && whence != 1 && whence != 2) {\n          throw new FS.ErrnoError(28);\n        }\n        stream.position = stream.stream_ops.llseek(stream, offset, whence);\n        stream.ungotten = [];\n        return stream.position;\n      },read:function(stream, buffer, offset, length, position) {\n        if (length < 0 || position < 0) {\n          throw new FS.ErrnoError(28);\n        }\n        if (FS.isClosed(stream)) {\n          throw new FS.ErrnoError(8);\n        }\n        if ((stream.flags & 2097155) === 1) {\n          throw new FS.ErrnoError(8);\n        }\n        if (FS.isDir(stream.node.mode)) {\n          throw new FS.ErrnoError(31);\n        }\n        if (!stream.stream_ops.read) {\n          throw new FS.ErrnoError(28);\n        }\n        var seeking = typeof position !== 'undefined';\n        if (!seeking) {\n          position = stream.position;\n        } else if (!stream.seekable) {\n          throw new FS.ErrnoError(70);\n        }\n        var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position);\n        if (!seeking) stream.position += bytesRead;\n        return bytesRead;\n      },write:function(stream, buffer, offset, length, position, canOwn) {\n        if (length < 0 || position < 0) {\n          throw new FS.ErrnoError(28);\n        }\n        if (FS.isClosed(stream)) {\n          throw new FS.ErrnoError(8);\n        }\n        if ((stream.flags & 2097155) === 0) {\n          throw new FS.ErrnoError(8);\n        }\n        if (FS.isDir(stream.node.mode)) {\n          throw new FS.ErrnoError(31);\n        }\n        if (!stream.stream_ops.write) {\n          throw new FS.ErrnoError(28);\n        }\n        if (stream.seekable && stream.flags & 1024) {\n          // seek to the end before writing in append mode\n          FS.llseek(stream, 0, 2);\n        }\n        var seeking = typeof position !== 'undefined';\n        if (!seeking) {\n          position = stream.position;\n        } else if (!stream.seekable) {\n          throw new FS.ErrnoError(70);\n        }\n        var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position, canOwn);\n        if (!seeking) stream.position += bytesWritten;\n        try {\n          if (stream.path && FS.trackingDelegate['onWriteToFile']) FS.trackingDelegate['onWriteToFile'](stream.path);\n        } catch(e) {\n          err(\"FS.trackingDelegate['onWriteToFile']('\"+stream.path+\"') threw an exception: \" + e.message);\n        }\n        return bytesWritten;\n      },allocate:function(stream, offset, length) {\n        if (FS.isClosed(stream)) {\n          throw new FS.ErrnoError(8);\n        }\n        if (offset < 0 || length <= 0) {\n          throw new FS.ErrnoError(28);\n        }\n        if ((stream.flags & 2097155) === 0) {\n          throw new FS.ErrnoError(8);\n        }\n        if (!FS.isFile(stream.node.mode) && !FS.isDir(stream.node.mode)) {\n          throw new FS.ErrnoError(43);\n        }\n        if (!stream.stream_ops.allocate) {\n          throw new FS.ErrnoError(138);\n        }\n        stream.stream_ops.allocate(stream, offset, length);\n      },mmap:function(stream, address, length, position, prot, flags) {\n        // User requests writing to file (prot & PROT_WRITE != 0).\n        // Checking if we have permissions to write to the file unless\n        // MAP_PRIVATE flag is set. According to POSIX spec it is possible\n        // to write to file opened in read-only mode with MAP_PRIVATE flag,\n        // as all modifications will be visible only in the memory of\n        // the current process.\n        if ((prot & 2) !== 0\n            && (flags & 2) === 0\n            && (stream.flags & 2097155) !== 2) {\n          throw new FS.ErrnoError(2);\n        }\n        if ((stream.flags & 2097155) === 1) {\n          throw new FS.ErrnoError(2);\n        }\n        if (!stream.stream_ops.mmap) {\n          throw new FS.ErrnoError(43);\n        }\n        return stream.stream_ops.mmap(stream, address, length, position, prot, flags);\n      },msync:function(stream, buffer, offset, length, mmapFlags) {\n        if (!stream || !stream.stream_ops.msync) {\n          return 0;\n        }\n        return stream.stream_ops.msync(stream, buffer, offset, length, mmapFlags);\n      },munmap:function(stream) {\n        return 0;\n      },ioctl:function(stream, cmd, arg) {\n        if (!stream.stream_ops.ioctl) {\n          throw new FS.ErrnoError(59);\n        }\n        return stream.stream_ops.ioctl(stream, cmd, arg);\n      },readFile:function(path, opts) {\n        opts = opts || {};\n        opts.flags = opts.flags || 0;\n        opts.encoding = opts.encoding || 'binary';\n        if (opts.encoding !== 'utf8' && opts.encoding !== 'binary') {\n          throw new Error('Invalid encoding type \"' + opts.encoding + '\"');\n        }\n        var ret;\n        var stream = FS.open(path, opts.flags);\n        var stat = FS.stat(path);\n        var length = stat.size;\n        var buf = new Uint8Array(length);\n        FS.read(stream, buf, 0, length, 0);\n        if (opts.encoding === 'utf8') {\n          ret = UTF8ArrayToString(buf, 0);\n        } else if (opts.encoding === 'binary') {\n          ret = buf;\n        }\n        FS.close(stream);\n        return ret;\n      },writeFile:function(path, data, opts) {\n        opts = opts || {};\n        opts.flags = opts.flags || 577;\n        var stream = FS.open(path, opts.flags, opts.mode);\n        if (typeof data === 'string') {\n          var buf = new Uint8Array(lengthBytesUTF8(data)+1);\n          var actualNumBytes = stringToUTF8Array(data, buf, 0, buf.length);\n          FS.write(stream, buf, 0, actualNumBytes, undefined, opts.canOwn);\n        } else if (ArrayBuffer.isView(data)) {\n          FS.write(stream, data, 0, data.byteLength, undefined, opts.canOwn);\n        } else {\n          throw new Error('Unsupported data type');\n        }\n        FS.close(stream);\n      },cwd:function() {\n        return FS.currentPath;\n      },chdir:function(path) {\n        var lookup = FS.lookupPath(path, { follow: true });\n        if (lookup.node === null) {\n          throw new FS.ErrnoError(44);\n        }\n        if (!FS.isDir(lookup.node.mode)) {\n          throw new FS.ErrnoError(54);\n        }\n        var errCode = FS.nodePermissions(lookup.node, 'x');\n        if (errCode) {\n          throw new FS.ErrnoError(errCode);\n        }\n        FS.currentPath = lookup.path;\n      },createDefaultDirectories:function() {\n        FS.mkdir('/tmp');\n        FS.mkdir('/home');\n        FS.mkdir('/home/web_user');\n      },createDefaultDevices:function() {\n        // create /dev\n        FS.mkdir('/dev');\n        // setup /dev/null\n        FS.registerDevice(FS.makedev(1, 3), {\n          read: function() { return 0; },\n          write: function(stream, buffer, offset, length, pos) { return length; }\n        });\n        FS.mkdev('/dev/null', FS.makedev(1, 3));\n        // setup /dev/tty and /dev/tty1\n        // stderr needs to print output using err() rather than out()\n        // so we register a second tty just for it.\n        TTY.register(FS.makedev(5, 0), TTY.default_tty_ops);\n        TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops);\n        FS.mkdev('/dev/tty', FS.makedev(5, 0));\n        FS.mkdev('/dev/tty1', FS.makedev(6, 0));\n        // setup /dev/[u]random\n        var random_device = getRandomDevice();\n        FS.createDevice('/dev', 'random', random_device);\n        FS.createDevice('/dev', 'urandom', random_device);\n        // we're not going to emulate the actual shm device,\n        // just create the tmp dirs that reside in it commonly\n        FS.mkdir('/dev/shm');\n        FS.mkdir('/dev/shm/tmp');\n      },createSpecialDirectories:function() {\n        // create /proc/self/fd which allows /proc/self/fd/6 => readlink gives the\n        // name of the stream for fd 6 (see test_unistd_ttyname)\n        FS.mkdir('/proc');\n        var proc_self = FS.mkdir('/proc/self');\n        FS.mkdir('/proc/self/fd');\n        FS.mount({\n          mount: function() {\n            var node = FS.createNode(proc_self, 'fd', 16384 | 511 /* 0777 */, 73);\n            node.node_ops = {\n              lookup: function(parent, name) {\n                var fd = +name;\n                var stream = FS.getStream(fd);\n                if (!stream) throw new FS.ErrnoError(8);\n                var ret = {\n                  parent: null,\n                  mount: { mountpoint: 'fake' },\n                  node_ops: { readlink: function() { return stream.path } }\n                };\n                ret.parent = ret; // make it look like a simple root node\n                return ret;\n              }\n            };\n            return node;\n          }\n        }, {}, '/proc/self/fd');\n      },createStandardStreams:function() {\n        // TODO deprecate the old functionality of a single\n        // input / output callback and that utilizes FS.createDevice\n        // and instead require a unique set of stream ops\n  \n        // by default, we symlink the standard streams to the\n        // default tty devices. however, if the standard streams\n        // have been overwritten we create a unique device for\n        // them instead.\n        if (Module['stdin']) {\n          FS.createDevice('/dev', 'stdin', Module['stdin']);\n        } else {\n          FS.symlink('/dev/tty', '/dev/stdin');\n        }\n        if (Module['stdout']) {\n          FS.createDevice('/dev', 'stdout', null, Module['stdout']);\n        } else {\n          FS.symlink('/dev/tty', '/dev/stdout');\n        }\n        if (Module['stderr']) {\n          FS.createDevice('/dev', 'stderr', null, Module['stderr']);\n        } else {\n          FS.symlink('/dev/tty1', '/dev/stderr');\n        }\n  \n        // open default streams for the stdin, stdout and stderr devices\n        var stdin = FS.open('/dev/stdin', 0);\n        var stdout = FS.open('/dev/stdout', 1);\n        var stderr = FS.open('/dev/stderr', 1);\n        assert(stdin.fd === 0, 'invalid handle for stdin (' + stdin.fd + ')');\n        assert(stdout.fd === 1, 'invalid handle for stdout (' + stdout.fd + ')');\n        assert(stderr.fd === 2, 'invalid handle for stderr (' + stderr.fd + ')');\n      },ensureErrnoError:function() {\n        if (FS.ErrnoError) return;\n        FS.ErrnoError = /** @this{Object} */ function ErrnoError(errno, node) {\n          this.node = node;\n          this.setErrno = /** @this{Object} */ function(errno) {\n            this.errno = errno;\n            for (var key in ERRNO_CODES) {\n              if (ERRNO_CODES[key] === errno) {\n                this.code = key;\n                break;\n              }\n            }\n          };\n          this.setErrno(errno);\n          this.message = ERRNO_MESSAGES[errno];\n  \n          // Try to get a maximally helpful stack trace. On Node.js, getting Error.stack\n          // now ensures it shows what we want.\n          if (this.stack) {\n            // Define the stack property for Node.js 4, which otherwise errors on the next line.\n            Object.defineProperty(this, \"stack\", { value: (new Error).stack, writable: true });\n            this.stack = demangleAll(this.stack);\n          }\n        };\n        FS.ErrnoError.prototype = new Error();\n        FS.ErrnoError.prototype.constructor = FS.ErrnoError;\n        // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info)\n        [44].forEach(function(code) {\n          FS.genericErrors[code] = new FS.ErrnoError(code);\n          FS.genericErrors[code].stack = '<generic error, no stack>';\n        });\n      },staticInit:function() {\n        FS.ensureErrnoError();\n  \n        FS.nameTable = new Array(4096);\n  \n        FS.mount(MEMFS, {}, '/');\n  \n        FS.createDefaultDirectories();\n        FS.createDefaultDevices();\n        FS.createSpecialDirectories();\n  \n        FS.filesystems = {\n          'MEMFS': MEMFS,\n        };\n      },init:function(input, output, error) {\n        assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)');\n        FS.init.initialized = true;\n  \n        FS.ensureErrnoError();\n  \n        // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here\n        Module['stdin'] = input || Module['stdin'];\n        Module['stdout'] = output || Module['stdout'];\n        Module['stderr'] = error || Module['stderr'];\n  \n        FS.createStandardStreams();\n      },quit:function() {\n        FS.init.initialized = false;\n        // force-flush all streams, so we get musl std streams printed out\n        var fflush = Module['_fflush'];\n        if (fflush) fflush(0);\n        // close all of our streams\n        for (var i = 0; i < FS.streams.length; i++) {\n          var stream = FS.streams[i];\n          if (!stream) {\n            continue;\n          }\n          FS.close(stream);\n        }\n      },getMode:function(canRead, canWrite) {\n        var mode = 0;\n        if (canRead) mode |= 292 | 73;\n        if (canWrite) mode |= 146;\n        return mode;\n      },findObject:function(path, dontResolveLastLink) {\n        var ret = FS.analyzePath(path, dontResolveLastLink);\n        if (ret.exists) {\n          return ret.object;\n        } else {\n          return null;\n        }\n      },analyzePath:function(path, dontResolveLastLink) {\n        // operate from within the context of the symlink's target\n        try {\n          var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });\n          path = lookup.path;\n        } catch (e) {\n        }\n        var ret = {\n          isRoot: false, exists: false, error: 0, name: null, path: null, object: null,\n          parentExists: false, parentPath: null, parentObject: null\n        };\n        try {\n          var lookup = FS.lookupPath(path, { parent: true });\n          ret.parentExists = true;\n          ret.parentPath = lookup.path;\n          ret.parentObject = lookup.node;\n          ret.name = PATH.basename(path);\n          lookup = FS.lookupPath(path, { follow: !dontResolveLastLink });\n          ret.exists = true;\n          ret.path = lookup.path;\n          ret.object = lookup.node;\n          ret.name = lookup.node.name;\n          ret.isRoot = lookup.path === '/';\n        } catch (e) {\n          ret.error = e.errno;\n        };\n        return ret;\n      },createPath:function(parent, path, canRead, canWrite) {\n        parent = typeof parent === 'string' ? parent : FS.getPath(parent);\n        var parts = path.split('/').reverse();\n        while (parts.length) {\n          var part = parts.pop();\n          if (!part) continue;\n          var current = PATH.join2(parent, part);\n          try {\n            FS.mkdir(current);\n          } catch (e) {\n            // ignore EEXIST\n          }\n          parent = current;\n        }\n        return current;\n      },createFile:function(parent, name, properties, canRead, canWrite) {\n        var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);\n        var mode = FS.getMode(canRead, canWrite);\n        return FS.create(path, mode);\n      },createDataFile:function(parent, name, data, canRead, canWrite, canOwn) {\n        var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent;\n        var mode = FS.getMode(canRead, canWrite);\n        var node = FS.create(path, mode);\n        if (data) {\n          if (typeof data === 'string') {\n            var arr = new Array(data.length);\n            for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i);\n            data = arr;\n          }\n          // make sure we can write to the file\n          FS.chmod(node, mode | 146);\n          var stream = FS.open(node, 577);\n          FS.write(stream, data, 0, data.length, 0, canOwn);\n          FS.close(stream);\n          FS.chmod(node, mode);\n        }\n        return node;\n      },createDevice:function(parent, name, input, output) {\n        var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name);\n        var mode = FS.getMode(!!input, !!output);\n        if (!FS.createDevice.major) FS.createDevice.major = 64;\n        var dev = FS.makedev(FS.createDevice.major++, 0);\n        // Create a fake device that a set of stream ops to emulate\n        // the old behavior.\n        FS.registerDevice(dev, {\n          open: function(stream) {\n            stream.seekable = false;\n          },\n          close: function(stream) {\n            // flush any pending line data\n            if (output && output.buffer && output.buffer.length) {\n              output(10);\n            }\n          },\n          read: function(stream, buffer, offset, length, pos /* ignored */) {\n            var bytesRead = 0;\n            for (var i = 0; i < length; i++) {\n              var result;\n              try {\n                result = input();\n              } catch (e) {\n                throw new FS.ErrnoError(29);\n              }\n              if (result === undefined && bytesRead === 0) {\n                throw new FS.ErrnoError(6);\n              }\n              if (result === null || result === undefined) break;\n              bytesRead++;\n              buffer[offset+i] = result;\n            }\n            if (bytesRead) {\n              stream.node.timestamp = Date.now();\n            }\n            return bytesRead;\n          },\n          write: function(stream, buffer, offset, length, pos) {\n            for (var i = 0; i < length; i++) {\n              try {\n                output(buffer[offset+i]);\n              } catch (e) {\n                throw new FS.ErrnoError(29);\n              }\n            }\n            if (length) {\n              stream.node.timestamp = Date.now();\n            }\n            return i;\n          }\n        });\n        return FS.mkdev(path, mode, dev);\n      },forceLoadFile:function(obj) {\n        if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true;\n        if (typeof XMLHttpRequest !== 'undefined') {\n          throw new Error(\"Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.\");\n        } else if (read_) {\n          // Command-line.\n          try {\n            // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as\n            //          read() will try to parse UTF8.\n            obj.contents = intArrayFromString(read_(obj.url), true);\n            obj.usedBytes = obj.contents.length;\n          } catch (e) {\n            throw new FS.ErrnoError(29);\n          }\n        } else {\n          throw new Error('Cannot load without read() or XMLHttpRequest.');\n        }\n      },createLazyFile:function(parent, name, url, canRead, canWrite) {\n        // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse.\n        /** @constructor */\n        function LazyUint8Array() {\n          this.lengthKnown = false;\n          this.chunks = []; // Loaded chunks. Index is the chunk number\n        }\n        LazyUint8Array.prototype.get = /** @this{Object} */ function LazyUint8Array_get(idx) {\n          if (idx > this.length-1 || idx < 0) {\n            return undefined;\n          }\n          var chunkOffset = idx % this.chunkSize;\n          var chunkNum = (idx / this.chunkSize)|0;\n          return this.getter(chunkNum)[chunkOffset];\n        };\n        LazyUint8Array.prototype.setDataGetter = function LazyUint8Array_setDataGetter(getter) {\n          this.getter = getter;\n        };\n        LazyUint8Array.prototype.cacheLength = function LazyUint8Array_cacheLength() {\n          // Find length\n          var xhr = new XMLHttpRequest();\n          xhr.open('HEAD', url, false);\n          xhr.send(null);\n          if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error(\"Couldn't load \" + url + \". Status: \" + xhr.status);\n          var datalength = Number(xhr.getResponseHeader(\"Content-length\"));\n          var header;\n          var hasByteServing = (header = xhr.getResponseHeader(\"Accept-Ranges\")) && header === \"bytes\";\n          var usesGzip = (header = xhr.getResponseHeader(\"Content-Encoding\")) && header === \"gzip\";\n  \n          var chunkSize = 1024*1024; // Chunk size in bytes\n  \n          if (!hasByteServing) chunkSize = datalength;\n  \n          // Function to get a range from the remote URL.\n          var doXHR = (function(from, to) {\n            if (from > to) throw new Error(\"invalid range (\" + from + \", \" + to + \") or no bytes requested!\");\n            if (to > datalength-1) throw new Error(\"only \" + datalength + \" bytes available! programmer error!\");\n  \n            // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available.\n            var xhr = new XMLHttpRequest();\n            xhr.open('GET', url, false);\n            if (datalength !== chunkSize) xhr.setRequestHeader(\"Range\", \"bytes=\" + from + \"-\" + to);\n  \n            // Some hints to the browser that we want binary data.\n            if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer';\n            if (xhr.overrideMimeType) {\n              xhr.overrideMimeType('text/plain; charset=x-user-defined');\n            }\n  \n            xhr.send(null);\n            if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error(\"Couldn't load \" + url + \". Status: \" + xhr.status);\n            if (xhr.response !== undefined) {\n              return new Uint8Array(/** @type{Array<number>} */(xhr.response || []));\n            } else {\n              return intArrayFromString(xhr.responseText || '', true);\n            }\n          });\n          var lazyArray = this;\n          lazyArray.setDataGetter(function(chunkNum) {\n            var start = chunkNum * chunkSize;\n            var end = (chunkNum+1) * chunkSize - 1; // including this byte\n            end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block\n            if (typeof(lazyArray.chunks[chunkNum]) === \"undefined\") {\n              lazyArray.chunks[chunkNum] = doXHR(start, end);\n            }\n            if (typeof(lazyArray.chunks[chunkNum]) === \"undefined\") throw new Error(\"doXHR failed!\");\n            return lazyArray.chunks[chunkNum];\n          });\n  \n          if (usesGzip || !datalength) {\n            // if the server uses gzip or doesn't supply the length, we have to download the whole file to get the (uncompressed) length\n            chunkSize = datalength = 1; // this will force getter(0)/doXHR do download the whole file\n            datalength = this.getter(0).length;\n            chunkSize = datalength;\n            out(\"LazyFiles on gzip forces download of the whole file when length is accessed\");\n          }\n  \n          this._length = datalength;\n          this._chunkSize = chunkSize;\n          this.lengthKnown = true;\n        };\n        if (typeof XMLHttpRequest !== 'undefined') {\n          if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc';\n          var lazyArray = new LazyUint8Array();\n          Object.defineProperties(lazyArray, {\n            length: {\n              get: /** @this{Object} */ function() {\n                if(!this.lengthKnown) {\n                  this.cacheLength();\n                }\n                return this._length;\n              }\n            },\n            chunkSize: {\n              get: /** @this{Object} */ function() {\n                if(!this.lengthKnown) {\n                  this.cacheLength();\n                }\n                return this._chunkSize;\n              }\n            }\n          });\n  \n          var properties = { isDevice: false, contents: lazyArray };\n        } else {\n          var properties = { isDevice: false, url: url };\n        }\n  \n        var node = FS.createFile(parent, name, properties, canRead, canWrite);\n        // This is a total hack, but I want to get this lazy file code out of the\n        // core of MEMFS. If we want to keep this lazy file concept I feel it should\n        // be its own thin LAZYFS proxying calls to MEMFS.\n        if (properties.contents) {\n          node.contents = properties.contents;\n        } else if (properties.url) {\n          node.contents = null;\n          node.url = properties.url;\n        }\n        // Add a function that defers querying the file size until it is asked the first time.\n        Object.defineProperties(node, {\n          usedBytes: {\n            get: /** @this {FSNode} */ function() { return this.contents.length; }\n          }\n        });\n        // override each stream op with one that tries to force load the lazy file first\n        var stream_ops = {};\n        var keys = Object.keys(node.stream_ops);\n        keys.forEach(function(key) {\n          var fn = node.stream_ops[key];\n          stream_ops[key] = function forceLoadLazyFile() {\n            FS.forceLoadFile(node);\n            return fn.apply(null, arguments);\n          };\n        });\n        // use a custom read function\n        stream_ops.read = function stream_ops_read(stream, buffer, offset, length, position) {\n          FS.forceLoadFile(node);\n          var contents = stream.node.contents;\n          if (position >= contents.length)\n            return 0;\n          var size = Math.min(contents.length - position, length);\n          assert(size >= 0);\n          if (contents.slice) { // normal array\n            for (var i = 0; i < size; i++) {\n              buffer[offset + i] = contents[position + i];\n            }\n          } else {\n            for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR\n              buffer[offset + i] = contents.get(position + i);\n            }\n          }\n          return size;\n        };\n        node.stream_ops = stream_ops;\n        return node;\n      },createPreloadedFile:function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile, canOwn, preFinish) {\n        Browser.init(); // XXX perhaps this method should move onto Browser?\n        // TODO we should allow people to just pass in a complete filename instead\n        // of parent and name being that we just join them anyways\n        var fullname = name ? PATH_FS.resolve(PATH.join2(parent, name)) : parent;\n        var dep = getUniqueRunDependency('cp ' + fullname); // might have several active requests for the same fullname\n        function processData(byteArray) {\n          function finish(byteArray) {\n            if (preFinish) preFinish();\n            if (!dontCreateFile) {\n              FS.createDataFile(parent, name, byteArray, canRead, canWrite, canOwn);\n            }\n            if (onload) onload();\n            removeRunDependency(dep);\n          }\n          var handled = false;\n          Module['preloadPlugins'].forEach(function(plugin) {\n            if (handled) return;\n            if (plugin['canHandle'](fullname)) {\n              plugin['handle'](byteArray, fullname, finish, function() {\n                if (onerror) onerror();\n                removeRunDependency(dep);\n              });\n              handled = true;\n            }\n          });\n          if (!handled) finish(byteArray);\n        }\n        addRunDependency(dep);\n        if (typeof url == 'string') {\n          Browser.asyncLoad(url, function(byteArray) {\n            processData(byteArray);\n          }, onerror);\n        } else {\n          processData(url);\n        }\n      },indexedDB:function() {\n        return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;\n      },DB_NAME:function() {\n        return 'EM_FS_' + window.location.pathname;\n      },DB_VERSION:20,DB_STORE_NAME:\"FILE_DATA\",saveFilesToDB:function(paths, onload, onerror) {\n        onload = onload || function(){};\n        onerror = onerror || function(){};\n        var indexedDB = FS.indexedDB();\n        try {\n          var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);\n        } catch (e) {\n          return onerror(e);\n        }\n        openRequest.onupgradeneeded = function openRequest_onupgradeneeded() {\n          out('creating db');\n          var db = openRequest.result;\n          db.createObjectStore(FS.DB_STORE_NAME);\n        };\n        openRequest.onsuccess = function openRequest_onsuccess() {\n          var db = openRequest.result;\n          var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');\n          var files = transaction.objectStore(FS.DB_STORE_NAME);\n          var ok = 0, fail = 0, total = paths.length;\n          function finish() {\n            if (fail == 0) onload(); else onerror();\n          }\n          paths.forEach(function(path) {\n            var putRequest = files.put(FS.analyzePath(path).object.contents, path);\n            putRequest.onsuccess = function putRequest_onsuccess() { ok++; if (ok + fail == total) finish() };\n            putRequest.onerror = function putRequest_onerror() { fail++; if (ok + fail == total) finish() };\n          });\n          transaction.onerror = onerror;\n        };\n        openRequest.onerror = onerror;\n      },loadFilesFromDB:function(paths, onload, onerror) {\n        onload = onload || function(){};\n        onerror = onerror || function(){};\n        var indexedDB = FS.indexedDB();\n        try {\n          var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);\n        } catch (e) {\n          return onerror(e);\n        }\n        openRequest.onupgradeneeded = onerror; // no database to load from\n        openRequest.onsuccess = function openRequest_onsuccess() {\n          var db = openRequest.result;\n          try {\n            var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');\n          } catch(e) {\n            onerror(e);\n            return;\n          }\n          var files = transaction.objectStore(FS.DB_STORE_NAME);\n          var ok = 0, fail = 0, total = paths.length;\n          function finish() {\n            if (fail == 0) onload(); else onerror();\n          }\n          paths.forEach(function(path) {\n            var getRequest = files.get(path);\n            getRequest.onsuccess = function getRequest_onsuccess() {\n              if (FS.analyzePath(path).exists) {\n                FS.unlink(path);\n              }\n              FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true);\n              ok++;\n              if (ok + fail == total) finish();\n            };\n            getRequest.onerror = function getRequest_onerror() { fail++; if (ok + fail == total) finish() };\n          });\n          transaction.onerror = onerror;\n        };\n        openRequest.onerror = onerror;\n      },absolutePath:function() {\n        abort('FS.absolutePath has been removed; use PATH_FS.resolve instead');\n      },createFolder:function() {\n        abort('FS.createFolder has been removed; use FS.mkdir instead');\n      },createLink:function() {\n        abort('FS.createLink has been removed; use FS.symlink instead');\n      },joinPath:function() {\n        abort('FS.joinPath has been removed; use PATH.join instead');\n      },mmapAlloc:function() {\n        abort('FS.mmapAlloc has been replaced by the top level function mmapAlloc');\n      },standardizePath:function() {\n        abort('FS.standardizePath has been removed; use PATH.normalize instead');\n      }};\n  var SYSCALLS={mappings:{},DEFAULT_POLLMASK:5,umask:511,calculateAt:function(dirfd, path, allowEmpty) {\n        if (path[0] === '/') {\n          return path;\n        }\n        // relative path\n        var dir;\n        if (dirfd === -100) {\n          dir = FS.cwd();\n        } else {\n          var dirstream = FS.getStream(dirfd);\n          if (!dirstream) throw new FS.ErrnoError(8);\n          dir = dirstream.path;\n        }\n        if (path.length == 0) {\n          if (!allowEmpty) {\n            throw new FS.ErrnoError(44);;\n          }\n          return dir;\n        }\n        return PATH.join2(dir, path);\n      },doStat:function(func, path, buf) {\n        try {\n          var stat = func(path);\n        } catch (e) {\n          if (e && e.node && PATH.normalize(path) !== PATH.normalize(FS.getPath(e.node))) {\n            // an error occurred while trying to look up the path; we should just report ENOTDIR\n            return -54;\n          }\n          throw e;\n        }\n        HEAP32[((buf)>>2)] = stat.dev;\n        HEAP32[(((buf)+(4))>>2)] = 0;\n        HEAP32[(((buf)+(8))>>2)] = stat.ino;\n        HEAP32[(((buf)+(12))>>2)] = stat.mode;\n        HEAP32[(((buf)+(16))>>2)] = stat.nlink;\n        HEAP32[(((buf)+(20))>>2)] = stat.uid;\n        HEAP32[(((buf)+(24))>>2)] = stat.gid;\n        HEAP32[(((buf)+(28))>>2)] = stat.rdev;\n        HEAP32[(((buf)+(32))>>2)] = 0;\n        (tempI64 = [stat.size>>>0,(tempDouble=stat.size,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[(((buf)+(40))>>2)] = tempI64[0],HEAP32[(((buf)+(44))>>2)] = tempI64[1]);\n        HEAP32[(((buf)+(48))>>2)] = 4096;\n        HEAP32[(((buf)+(52))>>2)] = stat.blocks;\n        HEAP32[(((buf)+(56))>>2)] = (stat.atime.getTime() / 1000)|0;\n        HEAP32[(((buf)+(60))>>2)] = 0;\n        HEAP32[(((buf)+(64))>>2)] = (stat.mtime.getTime() / 1000)|0;\n        HEAP32[(((buf)+(68))>>2)] = 0;\n        HEAP32[(((buf)+(72))>>2)] = (stat.ctime.getTime() / 1000)|0;\n        HEAP32[(((buf)+(76))>>2)] = 0;\n        (tempI64 = [stat.ino>>>0,(tempDouble=stat.ino,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[(((buf)+(80))>>2)] = tempI64[0],HEAP32[(((buf)+(84))>>2)] = tempI64[1]);\n        return 0;\n      },doMsync:function(addr, stream, len, flags, offset) {\n        var buffer = HEAPU8.slice(addr, addr + len);\n        FS.msync(stream, buffer, offset, len, flags);\n      },doMkdir:function(path, mode) {\n        // remove a trailing slash, if one - /a/b/ has basename of '', but\n        // we want to create b in the context of this function\n        path = PATH.normalize(path);\n        if (path[path.length-1] === '/') path = path.substr(0, path.length-1);\n        FS.mkdir(path, mode, 0);\n        return 0;\n      },doMknod:function(path, mode, dev) {\n        // we don't want this in the JS API as it uses mknod to create all nodes.\n        switch (mode & 61440) {\n          case 32768:\n          case 8192:\n          case 24576:\n          case 4096:\n          case 49152:\n            break;\n          default: return -28;\n        }\n        FS.mknod(path, mode, dev);\n        return 0;\n      },doReadlink:function(path, buf, bufsize) {\n        if (bufsize <= 0) return -28;\n        var ret = FS.readlink(path);\n  \n        var len = Math.min(bufsize, lengthBytesUTF8(ret));\n        var endChar = HEAP8[buf+len];\n        stringToUTF8(ret, buf, bufsize+1);\n        // readlink is one of the rare functions that write out a C string, but does never append a null to the output buffer(!)\n        // stringToUTF8() always appends a null byte, so restore the character under the null byte after the write.\n        HEAP8[buf+len] = endChar;\n  \n        return len;\n      },doAccess:function(path, amode) {\n        if (amode & ~7) {\n          // need a valid mode\n          return -28;\n        }\n        var node;\n        var lookup = FS.lookupPath(path, { follow: true });\n        node = lookup.node;\n        if (!node) {\n          return -44;\n        }\n        var perms = '';\n        if (amode & 4) perms += 'r';\n        if (amode & 2) perms += 'w';\n        if (amode & 1) perms += 'x';\n        if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) {\n          return -2;\n        }\n        return 0;\n      },doDup:function(path, flags, suggestFD) {\n        var suggest = FS.getStream(suggestFD);\n        if (suggest) FS.close(suggest);\n        return FS.open(path, flags, 0, suggestFD, suggestFD).fd;\n      },doReadv:function(stream, iov, iovcnt, offset) {\n        var ret = 0;\n        for (var i = 0; i < iovcnt; i++) {\n          var ptr = HEAP32[(((iov)+(i*8))>>2)];\n          var len = HEAP32[(((iov)+(i*8 + 4))>>2)];\n          var curr = FS.read(stream, HEAP8,ptr, len, offset);\n          if (curr < 0) return -1;\n          ret += curr;\n          if (curr < len) break; // nothing more to read\n        }\n        return ret;\n      },doWritev:function(stream, iov, iovcnt, offset) {\n        var ret = 0;\n        for (var i = 0; i < iovcnt; i++) {\n          var ptr = HEAP32[(((iov)+(i*8))>>2)];\n          var len = HEAP32[(((iov)+(i*8 + 4))>>2)];\n          var curr = FS.write(stream, HEAP8,ptr, len, offset);\n          if (curr < 0) return -1;\n          ret += curr;\n        }\n        return ret;\n      },varargs:undefined,get:function() {\n        assert(SYSCALLS.varargs != undefined);\n        SYSCALLS.varargs += 4;\n        var ret = HEAP32[(((SYSCALLS.varargs)-(4))>>2)];\n        return ret;\n      },getStr:function(ptr) {\n        var ret = UTF8ToString(ptr);\n        return ret;\n      },getStreamFromFD:function(fd) {\n        var stream = FS.getStream(fd);\n        if (!stream) throw new FS.ErrnoError(8);\n        return stream;\n      },get64:function(low, high) {\n        if (low >= 0) assert(high === 0);\n        else assert(high === -1);\n        return low;\n      }};\n  function ___sys_fcntl64(fd, cmd, varargs) {SYSCALLS.varargs = varargs;\n  try {\n  \n      var stream = SYSCALLS.getStreamFromFD(fd);\n      switch (cmd) {\n        case 0: {\n          var arg = SYSCALLS.get();\n          if (arg < 0) {\n            return -28;\n          }\n          var newStream;\n          newStream = FS.open(stream.path, stream.flags, 0, arg);\n          return newStream.fd;\n        }\n        case 1:\n        case 2:\n          return 0;  // FD_CLOEXEC makes no sense for a single process.\n        case 3:\n          return stream.flags;\n        case 4: {\n          var arg = SYSCALLS.get();\n          stream.flags |= arg;\n          return 0;\n        }\n        case 12:\n        /* case 12: Currently in musl F_GETLK64 has same value as F_GETLK, so omitted to avoid duplicate case blocks. If that changes, uncomment this */ {\n          \n          var arg = SYSCALLS.get();\n          var offset = 0;\n          // We're always unlocked.\n          HEAP16[(((arg)+(offset))>>1)] = 2;\n          return 0;\n        }\n        case 13:\n        case 14:\n        /* case 13: Currently in musl F_SETLK64 has same value as F_SETLK, so omitted to avoid duplicate case blocks. If that changes, uncomment this */\n        /* case 14: Currently in musl F_SETLKW64 has same value as F_SETLKW, so omitted to avoid duplicate case blocks. If that changes, uncomment this */\n          \n          \n          return 0; // Pretend that the locking is successful.\n        case 16:\n        case 8:\n          return -28; // These are for sockets. We don't have them fully implemented yet.\n        case 9:\n          // musl trusts getown return values, due to a bug where they must be, as they overlap with errors. just return -1 here, so fnctl() returns that, and we set errno ourselves.\n          setErrNo(28);\n          return -1;\n        default: {\n          return -28;\n        }\n      }\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return -e.errno;\n  }\n  }\n\n  function ___sys_ioctl(fd, op, varargs) {SYSCALLS.varargs = varargs;\n  try {\n  \n      var stream = SYSCALLS.getStreamFromFD(fd);\n      switch (op) {\n        case 21509:\n        case 21505: {\n          if (!stream.tty) return -59;\n          return 0;\n        }\n        case 21510:\n        case 21511:\n        case 21512:\n        case 21506:\n        case 21507:\n        case 21508: {\n          if (!stream.tty) return -59;\n          return 0; // no-op, not actually adjusting terminal settings\n        }\n        case 21519: {\n          if (!stream.tty) return -59;\n          var argp = SYSCALLS.get();\n          HEAP32[((argp)>>2)] = 0;\n          return 0;\n        }\n        case 21520: {\n          if (!stream.tty) return -59;\n          return -28; // not supported\n        }\n        case 21531: {\n          var argp = SYSCALLS.get();\n          return FS.ioctl(stream, op, argp);\n        }\n        case 21523: {\n          // TODO: in theory we should write to the winsize struct that gets\n          // passed in, but for now musl doesn't read anything on it\n          if (!stream.tty) return -59;\n          return 0;\n        }\n        case 21524: {\n          // TODO: technically, this ioctl call should change the window size.\n          // but, since emscripten doesn't have any concept of a terminal window\n          // yet, we'll just silently throw it away as we do TIOCGWINSZ\n          if (!stream.tty) return -59;\n          return 0;\n        }\n        default: abort('bad ioctl syscall ' + op);\n      }\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return -e.errno;\n  }\n  }\n\n  function ___sys_open(path, flags, varargs) {SYSCALLS.varargs = varargs;\n  try {\n  \n      var pathname = SYSCALLS.getStr(path);\n      var mode = varargs ? SYSCALLS.get() : 0;\n      var stream = FS.open(pathname, flags, mode);\n      return stream.fd;\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return -e.errno;\n  }\n  }\n\n  var _emscripten_get_now;if (ENVIRONMENT_IS_NODE) {\n    _emscripten_get_now = function() {\n      var t = process['hrtime']();\n      return t[0] * 1e3 + t[1] / 1e6;\n    };\n  } else if (typeof dateNow !== 'undefined') {\n    _emscripten_get_now = dateNow;\n  } else _emscripten_get_now = function() { return performance.now(); }\n  ;\n  \n  var _emscripten_get_now_is_monotonic=true;;\n  function _clock_gettime(clk_id, tp) {\n      // int clock_gettime(clockid_t clk_id, struct timespec *tp);\n      var now;\n      if (clk_id === 0) {\n        now = Date.now();\n      } else if ((clk_id === 1 || clk_id === 4) && _emscripten_get_now_is_monotonic) {\n        now = _emscripten_get_now();\n      } else {\n        setErrNo(28);\n        return -1;\n      }\n      HEAP32[((tp)>>2)] = (now/1000)|0; // seconds\n      HEAP32[(((tp)+(4))>>2)] = ((now % 1000)*1000*1000)|0; // nanoseconds\n      return 0;\n    }\n\n  function _dlclose(handle) {\n      abort(\"To use dlopen, you need to use Emscripten's linking support, see https://github.com/emscripten-core/emscripten/wiki/Linking\");\n    }\n\n  function _emscripten_set_main_loop_timing(mode, value) {\n      Browser.mainLoop.timingMode = mode;\n      Browser.mainLoop.timingValue = value;\n  \n      if (!Browser.mainLoop.func) {\n        console.error('emscripten_set_main_loop_timing: Cannot set timing mode for main loop since a main loop does not exist! Call emscripten_set_main_loop first to set one up.');\n        return 1; // Return non-zero on failure, can't set timing mode when there is no main loop.\n      }\n  \n      if (mode == 0 /*EM_TIMING_SETTIMEOUT*/) {\n        Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setTimeout() {\n          var timeUntilNextTick = Math.max(0, Browser.mainLoop.tickStartTime + value - _emscripten_get_now())|0;\n          setTimeout(Browser.mainLoop.runner, timeUntilNextTick); // doing this each time means that on exception, we stop\n        };\n        Browser.mainLoop.method = 'timeout';\n      } else if (mode == 1 /*EM_TIMING_RAF*/) {\n        Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_rAF() {\n          Browser.requestAnimationFrame(Browser.mainLoop.runner);\n        };\n        Browser.mainLoop.method = 'rAF';\n      } else if (mode == 2 /*EM_TIMING_SETIMMEDIATE*/) {\n        if (typeof setImmediate === 'undefined') {\n          // Emulate setImmediate. (note: not a complete polyfill, we don't emulate clearImmediate() to keep code size to minimum, since not needed)\n          var setImmediates = [];\n          var emscriptenMainLoopMessageId = 'setimmediate';\n          var Browser_setImmediate_messageHandler = function(event) {\n            // When called in current thread or Worker, the main loop ID is structured slightly different to accommodate for --proxy-to-worker runtime listening to Worker events,\n            // so check for both cases.\n            if (event.data === emscriptenMainLoopMessageId || event.data.target === emscriptenMainLoopMessageId) {\n              event.stopPropagation();\n              setImmediates.shift()();\n            }\n          }\n          addEventListener(\"message\", Browser_setImmediate_messageHandler, true);\n          setImmediate = /** @type{function(function(): ?, ...?): number} */(function Browser_emulated_setImmediate(func) {\n            setImmediates.push(func);\n            if (ENVIRONMENT_IS_WORKER) {\n              if (Module['setImmediates'] === undefined) Module['setImmediates'] = [];\n              Module['setImmediates'].push(func);\n              postMessage({target: emscriptenMainLoopMessageId}); // In --proxy-to-worker, route the message via proxyClient.js\n            } else postMessage(emscriptenMainLoopMessageId, \"*\"); // On the main thread, can just send the message to itself.\n          })\n        }\n        Browser.mainLoop.scheduler = function Browser_mainLoop_scheduler_setImmediate() {\n          setImmediate(Browser.mainLoop.runner);\n        };\n        Browser.mainLoop.method = 'immediate';\n      }\n      return 0;\n    }\n  function setMainLoop(browserIterationFunc, fps, simulateInfiniteLoop, arg, noSetTiming) {\n      noExitRuntime = true;\n  \n      assert(!Browser.mainLoop.func, 'emscripten_set_main_loop: there can only be one main loop function at once: call emscripten_cancel_main_loop to cancel the previous one before setting a new one with different parameters.');\n  \n      Browser.mainLoop.func = browserIterationFunc;\n      Browser.mainLoop.arg = arg;\n  \n      var thisMainLoopId = Browser.mainLoop.currentlyRunningMainloop;\n  \n      Browser.mainLoop.runner = function Browser_mainLoop_runner() {\n        if (ABORT) return;\n        if (Browser.mainLoop.queue.length > 0) {\n          var start = Date.now();\n          var blocker = Browser.mainLoop.queue.shift();\n          blocker.func(blocker.arg);\n          if (Browser.mainLoop.remainingBlockers) {\n            var remaining = Browser.mainLoop.remainingBlockers;\n            var next = remaining%1 == 0 ? remaining-1 : Math.floor(remaining);\n            if (blocker.counted) {\n              Browser.mainLoop.remainingBlockers = next;\n            } else {\n              // not counted, but move the progress along a tiny bit\n              next = next + 0.5; // do not steal all the next one's progress\n              Browser.mainLoop.remainingBlockers = (8*remaining + next)/9;\n            }\n          }\n          console.log('main loop blocker \"' + blocker.name + '\" took ' + (Date.now() - start) + ' ms'); //, left: ' + Browser.mainLoop.remainingBlockers);\n          Browser.mainLoop.updateStatus();\n  \n          // catches pause/resume main loop from blocker execution\n          if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return;\n  \n          setTimeout(Browser.mainLoop.runner, 0);\n          return;\n        }\n  \n        // catch pauses from non-main loop sources\n        if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return;\n  \n        // Implement very basic swap interval control\n        Browser.mainLoop.currentFrameNumber = Browser.mainLoop.currentFrameNumber + 1 | 0;\n        if (Browser.mainLoop.timingMode == 1/*EM_TIMING_RAF*/ && Browser.mainLoop.timingValue > 1 && Browser.mainLoop.currentFrameNumber % Browser.mainLoop.timingValue != 0) {\n          // Not the scheduled time to render this frame - skip.\n          Browser.mainLoop.scheduler();\n          return;\n        } else if (Browser.mainLoop.timingMode == 0/*EM_TIMING_SETTIMEOUT*/) {\n          Browser.mainLoop.tickStartTime = _emscripten_get_now();\n        }\n  \n        // Signal GL rendering layer that processing of a new frame is about to start. This helps it optimize\n        // VBO double-buffering and reduce GPU stalls.\n  \n        if (Browser.mainLoop.method === 'timeout' && Module.ctx) {\n          warnOnce('Looks like you are rendering without using requestAnimationFrame for the main loop. You should use 0 for the frame rate in emscripten_set_main_loop in order to use requestAnimationFrame, as that can greatly improve your frame rates!');\n          Browser.mainLoop.method = ''; // just warn once per call to set main loop\n        }\n  \n        Browser.mainLoop.runIter(browserIterationFunc);\n  \n        checkStackCookie();\n  \n        // catch pauses from the main loop itself\n        if (thisMainLoopId < Browser.mainLoop.currentlyRunningMainloop) return;\n  \n        // Queue new audio data. This is important to be right after the main loop invocation, so that we will immediately be able\n        // to queue the newest produced audio samples.\n        // TODO: Consider adding pre- and post- rAF callbacks so that GL.newRenderingFrameStarted() and SDL.audio.queueNewAudioData()\n        //       do not need to be hardcoded into this function, but can be more generic.\n        if (typeof SDL === 'object' && SDL.audio && SDL.audio.queueNewAudioData) SDL.audio.queueNewAudioData();\n  \n        Browser.mainLoop.scheduler();\n      }\n  \n      if (!noSetTiming) {\n        if (fps && fps > 0) _emscripten_set_main_loop_timing(0/*EM_TIMING_SETTIMEOUT*/, 1000.0 / fps);\n        else _emscripten_set_main_loop_timing(1/*EM_TIMING_RAF*/, 1); // Do rAF by rendering each frame (no decimating)\n  \n        Browser.mainLoop.scheduler();\n      }\n  \n      if (simulateInfiniteLoop) {\n        throw 'unwind';\n      }\n    }\n  var Browser={mainLoop:{scheduler:null,method:\"\",currentlyRunningMainloop:0,func:null,arg:0,timingMode:0,timingValue:0,currentFrameNumber:0,queue:[],pause:function() {\n          Browser.mainLoop.scheduler = null;\n          // Incrementing this signals the previous main loop that it's now become old, and it must return.\n          Browser.mainLoop.currentlyRunningMainloop++;\n        },resume:function() {\n          Browser.mainLoop.currentlyRunningMainloop++;\n          var timingMode = Browser.mainLoop.timingMode;\n          var timingValue = Browser.mainLoop.timingValue;\n          var func = Browser.mainLoop.func;\n          Browser.mainLoop.func = null;\n          // do not set timing and call scheduler, we will do it on the next lines\n          setMainLoop(func, 0, false, Browser.mainLoop.arg, true);\n          _emscripten_set_main_loop_timing(timingMode, timingValue);\n          Browser.mainLoop.scheduler();\n        },updateStatus:function() {\n          if (Module['setStatus']) {\n            var message = Module['statusMessage'] || 'Please wait...';\n            var remaining = Browser.mainLoop.remainingBlockers;\n            var expected = Browser.mainLoop.expectedBlockers;\n            if (remaining) {\n              if (remaining < expected) {\n                Module['setStatus'](message + ' (' + (expected - remaining) + '/' + expected + ')');\n              } else {\n                Module['setStatus'](message);\n              }\n            } else {\n              Module['setStatus']('');\n            }\n          }\n        },runIter:function(func) {\n          if (ABORT) return;\n          if (Module['preMainLoop']) {\n            var preRet = Module['preMainLoop']();\n            if (preRet === false) {\n              return; // |return false| skips a frame\n            }\n          }\n          try {\n            func();\n          } catch (e) {\n            if (e instanceof ExitStatus) {\n              return;\n            } else if (e == 'unwind') {\n              return;\n            } else {\n              if (e && typeof e === 'object' && e.stack) err('exception thrown: ' + [e, e.stack]);\n              throw e;\n            }\n          }\n          if (Module['postMainLoop']) Module['postMainLoop']();\n        }},isFullscreen:false,pointerLock:false,moduleContextCreatedCallbacks:[],workers:[],init:function() {\n        if (!Module[\"preloadPlugins\"]) Module[\"preloadPlugins\"] = []; // needs to exist even in workers\n  \n        if (Browser.initted) return;\n        Browser.initted = true;\n  \n        try {\n          new Blob();\n          Browser.hasBlobConstructor = true;\n        } catch(e) {\n          Browser.hasBlobConstructor = false;\n          console.log(\"warning: no blob constructor, cannot create blobs with mimetypes\");\n        }\n        Browser.BlobBuilder = typeof MozBlobBuilder != \"undefined\" ? MozBlobBuilder : (typeof WebKitBlobBuilder != \"undefined\" ? WebKitBlobBuilder : (!Browser.hasBlobConstructor ? console.log(\"warning: no BlobBuilder\") : null));\n        Browser.URLObject = typeof window != \"undefined\" ? (window.URL ? window.URL : window.webkitURL) : undefined;\n        if (!Module.noImageDecoding && typeof Browser.URLObject === 'undefined') {\n          console.log(\"warning: Browser does not support creating object URLs. Built-in browser image decoding will not be available.\");\n          Module.noImageDecoding = true;\n        }\n  \n        // Support for plugins that can process preloaded files. You can add more of these to\n        // your app by creating and appending to Module.preloadPlugins.\n        //\n        // Each plugin is asked if it can handle a file based on the file's name. If it can,\n        // it is given the file's raw data. When it is done, it calls a callback with the file's\n        // (possibly modified) data. For example, a plugin might decompress a file, or it\n        // might create some side data structure for use later (like an Image element, etc.).\n  \n        var imagePlugin = {};\n        imagePlugin['canHandle'] = function imagePlugin_canHandle(name) {\n          return !Module.noImageDecoding && /\\.(jpg|jpeg|png|bmp)$/i.test(name);\n        };\n        imagePlugin['handle'] = function imagePlugin_handle(byteArray, name, onload, onerror) {\n          var b = null;\n          if (Browser.hasBlobConstructor) {\n            try {\n              b = new Blob([byteArray], { type: Browser.getMimetype(name) });\n              if (b.size !== byteArray.length) { // Safari bug #118630\n                // Safari's Blob can only take an ArrayBuffer\n                b = new Blob([(new Uint8Array(byteArray)).buffer], { type: Browser.getMimetype(name) });\n              }\n            } catch(e) {\n              warnOnce('Blob constructor present but fails: ' + e + '; falling back to blob builder');\n            }\n          }\n          if (!b) {\n            var bb = new Browser.BlobBuilder();\n            bb.append((new Uint8Array(byteArray)).buffer); // we need to pass a buffer, and must copy the array to get the right data range\n            b = bb.getBlob();\n          }\n          var url = Browser.URLObject.createObjectURL(b);\n          assert(typeof url == 'string', 'createObjectURL must return a url as a string');\n          var img = new Image();\n          img.onload = function img_onload() {\n            assert(img.complete, 'Image ' + name + ' could not be decoded');\n            var canvas = document.createElement('canvas');\n            canvas.width = img.width;\n            canvas.height = img.height;\n            var ctx = canvas.getContext('2d');\n            ctx.drawImage(img, 0, 0);\n            Module[\"preloadedImages\"][name] = canvas;\n            Browser.URLObject.revokeObjectURL(url);\n            if (onload) onload(byteArray);\n          };\n          img.onerror = function img_onerror(event) {\n            console.log('Image ' + url + ' could not be decoded');\n            if (onerror) onerror();\n          };\n          img.src = url;\n        };\n        Module['preloadPlugins'].push(imagePlugin);\n  \n        var audioPlugin = {};\n        audioPlugin['canHandle'] = function audioPlugin_canHandle(name) {\n          return !Module.noAudioDecoding && name.substr(-4) in { '.ogg': 1, '.wav': 1, '.mp3': 1 };\n        };\n        audioPlugin['handle'] = function audioPlugin_handle(byteArray, name, onload, onerror) {\n          var done = false;\n          function finish(audio) {\n            if (done) return;\n            done = true;\n            Module[\"preloadedAudios\"][name] = audio;\n            if (onload) onload(byteArray);\n          }\n          function fail() {\n            if (done) return;\n            done = true;\n            Module[\"preloadedAudios\"][name] = new Audio(); // empty shim\n            if (onerror) onerror();\n          }\n          if (Browser.hasBlobConstructor) {\n            try {\n              var b = new Blob([byteArray], { type: Browser.getMimetype(name) });\n            } catch(e) {\n              return fail();\n            }\n            var url = Browser.URLObject.createObjectURL(b); // XXX we never revoke this!\n            assert(typeof url == 'string', 'createObjectURL must return a url as a string');\n            var audio = new Audio();\n            audio.addEventListener('canplaythrough', function() { finish(audio) }, false); // use addEventListener due to chromium bug 124926\n            audio.onerror = function audio_onerror(event) {\n              if (done) return;\n              console.log('warning: browser could not fully decode audio ' + name + ', trying slower base64 approach');\n              function encode64(data) {\n                var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n                var PAD = '=';\n                var ret = '';\n                var leftchar = 0;\n                var leftbits = 0;\n                for (var i = 0; i < data.length; i++) {\n                  leftchar = (leftchar << 8) | data[i];\n                  leftbits += 8;\n                  while (leftbits >= 6) {\n                    var curr = (leftchar >> (leftbits-6)) & 0x3f;\n                    leftbits -= 6;\n                    ret += BASE[curr];\n                  }\n                }\n                if (leftbits == 2) {\n                  ret += BASE[(leftchar&3) << 4];\n                  ret += PAD + PAD;\n                } else if (leftbits == 4) {\n                  ret += BASE[(leftchar&0xf) << 2];\n                  ret += PAD;\n                }\n                return ret;\n              }\n              audio.src = 'data:audio/x-' + name.substr(-3) + ';base64,' + encode64(byteArray);\n              finish(audio); // we don't wait for confirmation this worked - but it's worth trying\n            };\n            audio.src = url;\n            // workaround for chrome bug 124926 - we do not always get oncanplaythrough or onerror\n            Browser.safeSetTimeout(function() {\n              finish(audio); // try to use it even though it is not necessarily ready to play\n            }, 10000);\n          } else {\n            return fail();\n          }\n        };\n        Module['preloadPlugins'].push(audioPlugin);\n  \n        // Canvas event setup\n  \n        function pointerLockChange() {\n          Browser.pointerLock = document['pointerLockElement'] === Module['canvas'] ||\n                                document['mozPointerLockElement'] === Module['canvas'] ||\n                                document['webkitPointerLockElement'] === Module['canvas'] ||\n                                document['msPointerLockElement'] === Module['canvas'];\n        }\n        var canvas = Module['canvas'];\n        if (canvas) {\n          // forced aspect ratio can be enabled by defining 'forcedAspectRatio' on Module\n          // Module['forcedAspectRatio'] = 4 / 3;\n  \n          canvas.requestPointerLock = canvas['requestPointerLock'] ||\n                                      canvas['mozRequestPointerLock'] ||\n                                      canvas['webkitRequestPointerLock'] ||\n                                      canvas['msRequestPointerLock'] ||\n                                      function(){};\n          canvas.exitPointerLock = document['exitPointerLock'] ||\n                                   document['mozExitPointerLock'] ||\n                                   document['webkitExitPointerLock'] ||\n                                   document['msExitPointerLock'] ||\n                                   function(){}; // no-op if function does not exist\n          canvas.exitPointerLock = canvas.exitPointerLock.bind(document);\n  \n          document.addEventListener('pointerlockchange', pointerLockChange, false);\n          document.addEventListener('mozpointerlockchange', pointerLockChange, false);\n          document.addEventListener('webkitpointerlockchange', pointerLockChange, false);\n          document.addEventListener('mspointerlockchange', pointerLockChange, false);\n  \n          if (Module['elementPointerLock']) {\n            canvas.addEventListener(\"click\", function(ev) {\n              if (!Browser.pointerLock && Module['canvas'].requestPointerLock) {\n                Module['canvas'].requestPointerLock();\n                ev.preventDefault();\n              }\n            }, false);\n          }\n        }\n      },createContext:function(canvas, useWebGL, setInModule, webGLContextAttributes) {\n        if (useWebGL && Module.ctx && canvas == Module.canvas) return Module.ctx; // no need to recreate GL context if it's already been created for this canvas.\n  \n        var ctx;\n        var contextHandle;\n        if (useWebGL) {\n          // For GLES2/desktop GL compatibility, adjust a few defaults to be different to WebGL defaults, so that they align better with the desktop defaults.\n          var contextAttributes = {\n            antialias: false,\n            alpha: false,\n            majorVersion: 1,\n          };\n  \n          if (webGLContextAttributes) {\n            for (var attribute in webGLContextAttributes) {\n              contextAttributes[attribute] = webGLContextAttributes[attribute];\n            }\n          }\n  \n          // This check of existence of GL is here to satisfy Closure compiler, which yells if variable GL is referenced below but GL object is not\n          // actually compiled in because application is not doing any GL operations. TODO: Ideally if GL is not being used, this function\n          // Browser.createContext() should not even be emitted.\n          if (typeof GL !== 'undefined') {\n            contextHandle = GL.createContext(canvas, contextAttributes);\n            if (contextHandle) {\n              ctx = GL.getContext(contextHandle).GLctx;\n            }\n          }\n        } else {\n          ctx = canvas.getContext('2d');\n        }\n  \n        if (!ctx) return null;\n  \n        if (setInModule) {\n          if (!useWebGL) assert(typeof GLctx === 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it');\n  \n          Module.ctx = ctx;\n          if (useWebGL) GL.makeContextCurrent(contextHandle);\n          Module.useWebGL = useWebGL;\n          Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() });\n          Browser.init();\n        }\n        return ctx;\n      },destroyContext:function(canvas, useWebGL, setInModule) {},fullscreenHandlersInstalled:false,lockPointer:undefined,resizeCanvas:undefined,requestFullscreen:function(lockPointer, resizeCanvas) {\n        Browser.lockPointer = lockPointer;\n        Browser.resizeCanvas = resizeCanvas;\n        if (typeof Browser.lockPointer === 'undefined') Browser.lockPointer = true;\n        if (typeof Browser.resizeCanvas === 'undefined') Browser.resizeCanvas = false;\n  \n        var canvas = Module['canvas'];\n        function fullscreenChange() {\n          Browser.isFullscreen = false;\n          var canvasContainer = canvas.parentNode;\n          if ((document['fullscreenElement'] || document['mozFullScreenElement'] ||\n               document['msFullscreenElement'] || document['webkitFullscreenElement'] ||\n               document['webkitCurrentFullScreenElement']) === canvasContainer) {\n            canvas.exitFullscreen = Browser.exitFullscreen;\n            if (Browser.lockPointer) canvas.requestPointerLock();\n            Browser.isFullscreen = true;\n            if (Browser.resizeCanvas) {\n              Browser.setFullscreenCanvasSize();\n            } else {\n              Browser.updateCanvasDimensions(canvas);\n            }\n          } else {\n            // remove the full screen specific parent of the canvas again to restore the HTML structure from before going full screen\n            canvasContainer.parentNode.insertBefore(canvas, canvasContainer);\n            canvasContainer.parentNode.removeChild(canvasContainer);\n  \n            if (Browser.resizeCanvas) {\n              Browser.setWindowedCanvasSize();\n            } else {\n              Browser.updateCanvasDimensions(canvas);\n            }\n          }\n          if (Module['onFullScreen']) Module['onFullScreen'](Browser.isFullscreen);\n          if (Module['onFullscreen']) Module['onFullscreen'](Browser.isFullscreen);\n        }\n  \n        if (!Browser.fullscreenHandlersInstalled) {\n          Browser.fullscreenHandlersInstalled = true;\n          document.addEventListener('fullscreenchange', fullscreenChange, false);\n          document.addEventListener('mozfullscreenchange', fullscreenChange, false);\n          document.addEventListener('webkitfullscreenchange', fullscreenChange, false);\n          document.addEventListener('MSFullscreenChange', fullscreenChange, false);\n        }\n  \n        // create a new parent to ensure the canvas has no siblings. this allows browsers to optimize full screen performance when its parent is the full screen root\n        var canvasContainer = document.createElement(\"div\");\n        canvas.parentNode.insertBefore(canvasContainer, canvas);\n        canvasContainer.appendChild(canvas);\n  \n        // use parent of canvas as full screen root to allow aspect ratio correction (Firefox stretches the root to screen size)\n        canvasContainer.requestFullscreen = canvasContainer['requestFullscreen'] ||\n                                            canvasContainer['mozRequestFullScreen'] ||\n                                            canvasContainer['msRequestFullscreen'] ||\n                                           (canvasContainer['webkitRequestFullscreen'] ? function() { canvasContainer['webkitRequestFullscreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null) ||\n                                           (canvasContainer['webkitRequestFullScreen'] ? function() { canvasContainer['webkitRequestFullScreen'](Element['ALLOW_KEYBOARD_INPUT']) } : null);\n  \n        canvasContainer.requestFullscreen();\n      },requestFullScreen:function() {\n        abort('Module.requestFullScreen has been replaced by Module.requestFullscreen (without a capital S)');\n      },exitFullscreen:function() {\n        // This is workaround for chrome. Trying to exit from fullscreen\n        // not in fullscreen state will cause \"TypeError: Document not active\"\n        // in chrome. See https://github.com/emscripten-core/emscripten/pull/8236\n        if (!Browser.isFullscreen) {\n          return false;\n        }\n  \n        var CFS = document['exitFullscreen'] ||\n                  document['cancelFullScreen'] ||\n                  document['mozCancelFullScreen'] ||\n                  document['msExitFullscreen'] ||\n                  document['webkitCancelFullScreen'] ||\n            (function() {});\n        CFS.apply(document, []);\n        return true;\n      },nextRAF:0,fakeRequestAnimationFrame:function(func) {\n        // try to keep 60fps between calls to here\n        var now = Date.now();\n        if (Browser.nextRAF === 0) {\n          Browser.nextRAF = now + 1000/60;\n        } else {\n          while (now + 2 >= Browser.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0\n            Browser.nextRAF += 1000/60;\n          }\n        }\n        var delay = Math.max(Browser.nextRAF - now, 0);\n        setTimeout(func, delay);\n      },requestAnimationFrame:function(func) {\n        if (typeof requestAnimationFrame === 'function') {\n          requestAnimationFrame(func);\n          return;\n        }\n        var RAF = Browser.fakeRequestAnimationFrame;\n        RAF(func);\n      },safeRequestAnimationFrame:function(func) {\n        return Browser.requestAnimationFrame(function() {\n          if (ABORT) return;\n          func();\n        });\n      },safeSetTimeout:function(func, timeout) {\n        noExitRuntime = true;\n        return setTimeout(function() {\n          if (ABORT) return;\n          func();\n        }, timeout);\n      },getMimetype:function(name) {\n        return {\n          'jpg': 'image/jpeg',\n          'jpeg': 'image/jpeg',\n          'png': 'image/png',\n          'bmp': 'image/bmp',\n          'ogg': 'audio/ogg',\n          'wav': 'audio/wav',\n          'mp3': 'audio/mpeg'\n        }[name.substr(name.lastIndexOf('.')+1)];\n      },getUserMedia:function(func) {\n        if(!window.getUserMedia) {\n          window.getUserMedia = navigator['getUserMedia'] ||\n                                navigator['mozGetUserMedia'];\n        }\n        window.getUserMedia(func);\n      },getMovementX:function(event) {\n        return event['movementX'] ||\n               event['mozMovementX'] ||\n               event['webkitMovementX'] ||\n               0;\n      },getMovementY:function(event) {\n        return event['movementY'] ||\n               event['mozMovementY'] ||\n               event['webkitMovementY'] ||\n               0;\n      },getMouseWheelDelta:function(event) {\n        var delta = 0;\n        switch (event.type) {\n          case 'DOMMouseScroll':\n            // 3 lines make up a step\n            delta = event.detail / 3;\n            break;\n          case 'mousewheel':\n            // 120 units make up a step\n            delta = event.wheelDelta / 120;\n            break;\n          case 'wheel':\n            delta = event.deltaY\n            switch(event.deltaMode) {\n              case 0:\n                // DOM_DELTA_PIXEL: 100 pixels make up a step\n                delta /= 100;\n                break;\n              case 1:\n                // DOM_DELTA_LINE: 3 lines make up a step\n                delta /= 3;\n                break;\n              case 2:\n                // DOM_DELTA_PAGE: A page makes up 80 steps\n                delta *= 80;\n                break;\n              default:\n                throw 'unrecognized mouse wheel delta mode: ' + event.deltaMode;\n            }\n            break;\n          default:\n            throw 'unrecognized mouse wheel event: ' + event.type;\n        }\n        return delta;\n      },mouseX:0,mouseY:0,mouseMovementX:0,mouseMovementY:0,touches:{},lastTouches:{},calculateMouseEvent:function(event) { // event should be mousemove, mousedown or mouseup\n        if (Browser.pointerLock) {\n          // When the pointer is locked, calculate the coordinates\n          // based on the movement of the mouse.\n          // Workaround for Firefox bug 764498\n          if (event.type != 'mousemove' &&\n              ('mozMovementX' in event)) {\n            Browser.mouseMovementX = Browser.mouseMovementY = 0;\n          } else {\n            Browser.mouseMovementX = Browser.getMovementX(event);\n            Browser.mouseMovementY = Browser.getMovementY(event);\n          }\n  \n          // check if SDL is available\n          if (typeof SDL != \"undefined\") {\n            Browser.mouseX = SDL.mouseX + Browser.mouseMovementX;\n            Browser.mouseY = SDL.mouseY + Browser.mouseMovementY;\n          } else {\n            // just add the mouse delta to the current absolut mouse position\n            // FIXME: ideally this should be clamped against the canvas size and zero\n            Browser.mouseX += Browser.mouseMovementX;\n            Browser.mouseY += Browser.mouseMovementY;\n          }\n        } else {\n          // Otherwise, calculate the movement based on the changes\n          // in the coordinates.\n          var rect = Module[\"canvas\"].getBoundingClientRect();\n          var cw = Module[\"canvas\"].width;\n          var ch = Module[\"canvas\"].height;\n  \n          // Neither .scrollX or .pageXOffset are defined in a spec, but\n          // we prefer .scrollX because it is currently in a spec draft.\n          // (see: http://www.w3.org/TR/2013/WD-cssom-view-20131217/)\n          var scrollX = ((typeof window.scrollX !== 'undefined') ? window.scrollX : window.pageXOffset);\n          var scrollY = ((typeof window.scrollY !== 'undefined') ? window.scrollY : window.pageYOffset);\n          // If this assert lands, it's likely because the browser doesn't support scrollX or pageXOffset\n          // and we have no viable fallback.\n          assert((typeof scrollX !== 'undefined') && (typeof scrollY !== 'undefined'), 'Unable to retrieve scroll position, mouse positions likely broken.');\n  \n          if (event.type === 'touchstart' || event.type === 'touchend' || event.type === 'touchmove') {\n            var touch = event.touch;\n            if (touch === undefined) {\n              return; // the \"touch\" property is only defined in SDL\n  \n            }\n            var adjustedX = touch.pageX - (scrollX + rect.left);\n            var adjustedY = touch.pageY - (scrollY + rect.top);\n  \n            adjustedX = adjustedX * (cw / rect.width);\n            adjustedY = adjustedY * (ch / rect.height);\n  \n            var coords = { x: adjustedX, y: adjustedY };\n  \n            if (event.type === 'touchstart') {\n              Browser.lastTouches[touch.identifier] = coords;\n              Browser.touches[touch.identifier] = coords;\n            } else if (event.type === 'touchend' || event.type === 'touchmove') {\n              var last = Browser.touches[touch.identifier];\n              if (!last) last = coords;\n              Browser.lastTouches[touch.identifier] = last;\n              Browser.touches[touch.identifier] = coords;\n            }\n            return;\n          }\n  \n          var x = event.pageX - (scrollX + rect.left);\n          var y = event.pageY - (scrollY + rect.top);\n  \n          // the canvas might be CSS-scaled compared to its backbuffer;\n          // SDL-using content will want mouse coordinates in terms\n          // of backbuffer units.\n          x = x * (cw / rect.width);\n          y = y * (ch / rect.height);\n  \n          Browser.mouseMovementX = x - Browser.mouseX;\n          Browser.mouseMovementY = y - Browser.mouseY;\n          Browser.mouseX = x;\n          Browser.mouseY = y;\n        }\n      },asyncLoad:function(url, onload, onerror, noRunDep) {\n        var dep = !noRunDep ? getUniqueRunDependency('al ' + url) : '';\n        readAsync(url, function(arrayBuffer) {\n          assert(arrayBuffer, 'Loading data file \"' + url + '\" failed (no arrayBuffer).');\n          onload(new Uint8Array(arrayBuffer));\n          if (dep) removeRunDependency(dep);\n        }, function(event) {\n          if (onerror) {\n            onerror();\n          } else {\n            throw 'Loading data file \"' + url + '\" failed.';\n          }\n        });\n        if (dep) addRunDependency(dep);\n      },resizeListeners:[],updateResizeListeners:function() {\n        var canvas = Module['canvas'];\n        Browser.resizeListeners.forEach(function(listener) {\n          listener(canvas.width, canvas.height);\n        });\n      },setCanvasSize:function(width, height, noUpdates) {\n        var canvas = Module['canvas'];\n        Browser.updateCanvasDimensions(canvas, width, height);\n        if (!noUpdates) Browser.updateResizeListeners();\n      },windowedWidth:0,windowedHeight:0,setFullscreenCanvasSize:function() {\n        // check if SDL is available\n        if (typeof SDL != \"undefined\") {\n          var flags = HEAPU32[((SDL.screen)>>2)];\n          flags = flags | 0x00800000; // set SDL_FULLSCREEN flag\n          HEAP32[((SDL.screen)>>2)] = flags\n        }\n        Browser.updateCanvasDimensions(Module['canvas']);\n        Browser.updateResizeListeners();\n      },setWindowedCanvasSize:function() {\n        // check if SDL is available\n        if (typeof SDL != \"undefined\") {\n          var flags = HEAPU32[((SDL.screen)>>2)];\n          flags = flags & ~0x00800000; // clear SDL_FULLSCREEN flag\n          HEAP32[((SDL.screen)>>2)] = flags\n        }\n        Browser.updateCanvasDimensions(Module['canvas']);\n        Browser.updateResizeListeners();\n      },updateCanvasDimensions:function(canvas, wNative, hNative) {\n        if (wNative && hNative) {\n          canvas.widthNative = wNative;\n          canvas.heightNative = hNative;\n        } else {\n          wNative = canvas.widthNative;\n          hNative = canvas.heightNative;\n        }\n        var w = wNative;\n        var h = hNative;\n        if (Module['forcedAspectRatio'] && Module['forcedAspectRatio'] > 0) {\n          if (w/h < Module['forcedAspectRatio']) {\n            w = Math.round(h * Module['forcedAspectRatio']);\n          } else {\n            h = Math.round(w / Module['forcedAspectRatio']);\n          }\n        }\n        if (((document['fullscreenElement'] || document['mozFullScreenElement'] ||\n             document['msFullscreenElement'] || document['webkitFullscreenElement'] ||\n             document['webkitCurrentFullScreenElement']) === canvas.parentNode) && (typeof screen != 'undefined')) {\n           var factor = Math.min(screen.width / w, screen.height / h);\n           w = Math.round(w * factor);\n           h = Math.round(h * factor);\n        }\n        if (Browser.resizeCanvas) {\n          if (canvas.width  != w) canvas.width  = w;\n          if (canvas.height != h) canvas.height = h;\n          if (typeof canvas.style != 'undefined') {\n            canvas.style.removeProperty( \"width\");\n            canvas.style.removeProperty(\"height\");\n          }\n        } else {\n          if (canvas.width  != wNative) canvas.width  = wNative;\n          if (canvas.height != hNative) canvas.height = hNative;\n          if (typeof canvas.style != 'undefined') {\n            if (w != wNative || h != hNative) {\n              canvas.style.setProperty( \"width\", w + \"px\", \"important\");\n              canvas.style.setProperty(\"height\", h + \"px\", \"important\");\n            } else {\n              canvas.style.removeProperty( \"width\");\n              canvas.style.removeProperty(\"height\");\n            }\n          }\n        }\n      },wgetRequests:{},nextWgetRequestHandle:0,getNextWgetRequestHandle:function() {\n        var handle = Browser.nextWgetRequestHandle;\n        Browser.nextWgetRequestHandle++;\n        return handle;\n      }};\n  var EGL={errorCode:12288,defaultDisplayInitialized:false,currentContext:0,currentReadSurface:0,currentDrawSurface:0,contextAttributes:{alpha:false,depth:false,stencil:false,antialias:false},stringCache:{},setErrorCode:function(code) {\n        EGL.errorCode = code;\n      },chooseConfig:function(display, attribList, config, config_size, numConfigs) {\n        if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n          EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n          return 0;\n        }\n  \n        if (attribList) {\n          // read attribList if it is non-null\n          for(;;) {\n            var param = HEAP32[((attribList)>>2)];\n            if (param == 0x3021 /*EGL_ALPHA_SIZE*/) {\n              var alphaSize = HEAP32[(((attribList)+(4))>>2)];\n              EGL.contextAttributes.alpha = (alphaSize > 0);\n            } else if (param == 0x3025 /*EGL_DEPTH_SIZE*/) {\n              var depthSize = HEAP32[(((attribList)+(4))>>2)];\n              EGL.contextAttributes.depth = (depthSize > 0);\n            } else if (param == 0x3026 /*EGL_STENCIL_SIZE*/) {\n              var stencilSize = HEAP32[(((attribList)+(4))>>2)];\n              EGL.contextAttributes.stencil = (stencilSize > 0);\n            } else if (param == 0x3031 /*EGL_SAMPLES*/) {\n              var samples = HEAP32[(((attribList)+(4))>>2)];\n              EGL.contextAttributes.antialias = (samples > 0);\n            } else if (param == 0x3032 /*EGL_SAMPLE_BUFFERS*/) {\n              var samples = HEAP32[(((attribList)+(4))>>2)];\n              EGL.contextAttributes.antialias = (samples == 1);\n            } else if (param == 0x3100 /*EGL_CONTEXT_PRIORITY_LEVEL_IMG*/) {\n              var requestedPriority = HEAP32[(((attribList)+(4))>>2)];\n              EGL.contextAttributes.lowLatency = (requestedPriority != 0x3103 /*EGL_CONTEXT_PRIORITY_LOW_IMG*/);\n            } else if (param == 0x3038 /*EGL_NONE*/) {\n                break;\n            }\n            attribList += 8;\n          }\n        }\n  \n        if ((!config || !config_size) && !numConfigs) {\n          EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);\n          return 0;\n        }\n        if (numConfigs) {\n          HEAP32[((numConfigs)>>2)] = 1; // Total number of supported configs: 1.\n        }\n        if (config && config_size > 0) {\n          HEAP32[((config)>>2)] = 62002;\n        }\n  \n        EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n        return 1;\n      }};\n  function _eglBindAPI(api) {\n      if (api == 0x30A0 /* EGL_OPENGL_ES_API */) {\n        EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n        return 1;\n      } else { // if (api == 0x30A1 /* EGL_OPENVG_API */ || api == 0x30A2 /* EGL_OPENGL_API */) {\n        EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);\n        return 0;\n      }\n    }\n\n  function _eglChooseConfig(display, attrib_list, configs, config_size, numConfigs) {\n      return EGL.chooseConfig(display, attrib_list, configs, config_size, numConfigs);\n    }\n\n  function __webgl_enable_ANGLE_instanced_arrays(ctx) {\n      // Extension available in WebGL 1 from Firefox 26 and Google Chrome 30 onwards. Core feature in WebGL 2.\n      var ext = ctx.getExtension('ANGLE_instanced_arrays');\n      if (ext) {\n        ctx['vertexAttribDivisor'] = function(index, divisor) { ext['vertexAttribDivisorANGLE'](index, divisor); };\n        ctx['drawArraysInstanced'] = function(mode, first, count, primcount) { ext['drawArraysInstancedANGLE'](mode, first, count, primcount); };\n        ctx['drawElementsInstanced'] = function(mode, count, type, indices, primcount) { ext['drawElementsInstancedANGLE'](mode, count, type, indices, primcount); };\n        return 1;\n      }\n    }\n  \n  function __webgl_enable_OES_vertex_array_object(ctx) {\n      // Extension available in WebGL 1 from Firefox 25 and WebKit 536.28/desktop Safari 6.0.3 onwards. Core feature in WebGL 2.\n      var ext = ctx.getExtension('OES_vertex_array_object');\n      if (ext) {\n        ctx['createVertexArray'] = function() { return ext['createVertexArrayOES'](); };\n        ctx['deleteVertexArray'] = function(vao) { ext['deleteVertexArrayOES'](vao); };\n        ctx['bindVertexArray'] = function(vao) { ext['bindVertexArrayOES'](vao); };\n        ctx['isVertexArray'] = function(vao) { return ext['isVertexArrayOES'](vao); };\n        return 1;\n      }\n    }\n  \n  function __webgl_enable_WEBGL_draw_buffers(ctx) {\n      // Extension available in WebGL 1 from Firefox 28 onwards. Core feature in WebGL 2.\n      var ext = ctx.getExtension('WEBGL_draw_buffers');\n      if (ext) {\n        ctx['drawBuffers'] = function(n, bufs) { ext['drawBuffersWEBGL'](n, bufs); };\n        return 1;\n      }\n    }\n  \n  function __webgl_enable_WEBGL_multi_draw(ctx) {\n      // Closure is expected to be allowed to minify the '.multiDrawWebgl' property, so not accessing it quoted.\n      return !!(ctx.multiDrawWebgl = ctx.getExtension('WEBGL_multi_draw'));\n    }\n  var GL={counter:1,buffers:[],programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},timerQueriesEXT:[],programInfos:{},stringCache:{},unpackAlignment:4,recordError:function recordError(errorCode) {\n        if (!GL.lastError) {\n          GL.lastError = errorCode;\n        }\n      },getNewId:function(table) {\n        var ret = GL.counter++;\n        for (var i = table.length; i < ret; i++) {\n          table[i] = null;\n        }\n        return ret;\n      },getSource:function(shader, count, string, length) {\n        var source = '';\n        for (var i = 0; i < count; ++i) {\n          var len = length ? HEAP32[(((length)+(i*4))>>2)] : -1;\n          source += UTF8ToString(HEAP32[(((string)+(i*4))>>2)], len < 0 ? undefined : len);\n        }\n        return source;\n      },createContext:function(canvas, webGLContextAttributes) {\n  \n        var ctx = \n          (canvas.getContext(\"webgl\", webGLContextAttributes)\n            // https://caniuse.com/#feat=webgl\n            );\n  \n        if (!ctx) return 0;\n  \n        var handle = GL.registerContext(ctx, webGLContextAttributes);\n  \n        return handle;\n      },registerContext:function(ctx, webGLContextAttributes) {\n        // without pthreads a context is just an integer ID\n        var handle = GL.getNewId(GL.contexts);\n  \n        var context = {\n          handle: handle,\n          attributes: webGLContextAttributes,\n          version: webGLContextAttributes.majorVersion,\n          GLctx: ctx\n        };\n  \n        // Store the created context object so that we can access the context given a canvas without having to pass the parameters again.\n        if (ctx.canvas) ctx.canvas.GLctxObject = context;\n        GL.contexts[handle] = context;\n        if (typeof webGLContextAttributes.enableExtensionsByDefault === 'undefined' || webGLContextAttributes.enableExtensionsByDefault) {\n          GL.initExtensions(context);\n        }\n  \n        return handle;\n      },makeContextCurrent:function(contextHandle) {\n  \n        GL.currentContext = GL.contexts[contextHandle]; // Active Emscripten GL layer context object.\n        Module.ctx = GLctx = GL.currentContext && GL.currentContext.GLctx; // Active WebGL context object.\n        return !(contextHandle && !GLctx);\n      },getContext:function(contextHandle) {\n        return GL.contexts[contextHandle];\n      },deleteContext:function(contextHandle) {\n        if (GL.currentContext === GL.contexts[contextHandle]) GL.currentContext = null;\n        if (typeof JSEvents === 'object') JSEvents.removeAllHandlersOnTarget(GL.contexts[contextHandle].GLctx.canvas); // Release all JS event handlers on the DOM element that the GL context is associated with since the context is now deleted.\n        if (GL.contexts[contextHandle] && GL.contexts[contextHandle].GLctx.canvas) GL.contexts[contextHandle].GLctx.canvas.GLctxObject = undefined; // Make sure the canvas object no longer refers to the context object so there are no GC surprises.\n        GL.contexts[contextHandle] = null;\n      },initExtensions:function(context) {\n        // If this function is called without a specific context object, init the extensions of the currently active context.\n        if (!context) context = GL.currentContext;\n  \n        if (context.initExtensionsDone) return;\n        context.initExtensionsDone = true;\n  \n        var GLctx = context.GLctx;\n  \n        // Detect the presence of a few extensions manually, this GL interop layer itself will need to know if they exist.\n  \n        // Extensions that are only available in WebGL 1 (the calls will be no-ops if called on a WebGL 2 context active)\n        __webgl_enable_ANGLE_instanced_arrays(GLctx);\n        __webgl_enable_OES_vertex_array_object(GLctx);\n        __webgl_enable_WEBGL_draw_buffers(GLctx);\n  \n        GLctx.disjointTimerQueryExt = GLctx.getExtension(\"EXT_disjoint_timer_query\");\n        __webgl_enable_WEBGL_multi_draw(GLctx);\n  \n        // .getSupportedExtensions() can return null if context is lost, so coerce to empty array.\n        var exts = GLctx.getSupportedExtensions() || [];\n        exts.forEach(function(ext) {\n          // WEBGL_lose_context, WEBGL_debug_renderer_info and WEBGL_debug_shaders are not enabled by default.\n          if (ext.indexOf('lose_context') < 0 && ext.indexOf('debug') < 0) {\n            // Call .getExtension() to enable that extension permanently.\n            GLctx.getExtension(ext);\n          }\n        });\n      },populateUniformTable:function(program) {\n        var p = GL.programs[program];\n        var ptable = GL.programInfos[program] = {\n          uniforms: {},\n          maxUniformLength: 0, // This is eagerly computed below, since we already enumerate all uniforms anyway.\n          maxAttributeLength: -1, // This is lazily computed and cached, computed when/if first asked, \"-1\" meaning not computed yet.\n          maxUniformBlockNameLength: -1 // Lazily computed as well\n        };\n  \n        var utable = ptable.uniforms;\n        // A program's uniform table maps the string name of an uniform to an integer location of that uniform.\n        // The global GL.uniforms map maps integer locations to WebGLUniformLocations.\n        var numUniforms = GLctx.getProgramParameter(p, 0x8B86/*GL_ACTIVE_UNIFORMS*/);\n        for (var i = 0; i < numUniforms; ++i) {\n          var u = GLctx.getActiveUniform(p, i);\n  \n          var name = u.name;\n          ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length+1);\n  \n          // If we are dealing with an array, e.g. vec4 foo[3], strip off the array index part to canonicalize that \"foo\", \"foo[]\",\n          // and \"foo[0]\" will mean the same. Loop below will populate foo[1] and foo[2].\n          if (name.slice(-1) == ']') {\n            name = name.slice(0, name.lastIndexOf('['));\n          }\n  \n          // Optimize memory usage slightly: If we have an array of uniforms, e.g. 'vec3 colors[3];', then\n          // only store the string 'colors' in utable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i.\n          // Note that for the GL.uniforms table, we still need to fetch the all WebGLUniformLocations for all the indices.\n          var loc = GLctx.getUniformLocation(p, name);\n          if (loc) {\n            var id = GL.getNewId(GL.uniforms);\n            utable[name] = [u.size, id];\n            GL.uniforms[id] = loc;\n  \n            for (var j = 1; j < u.size; ++j) {\n              var n = name + '['+j+']';\n              loc = GLctx.getUniformLocation(p, n);\n              id = GL.getNewId(GL.uniforms);\n  \n              GL.uniforms[id] = loc;\n            }\n          }\n        }\n      }};\n  function _eglCreateContext(display, config, hmm, contextAttribs) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n  \n      // EGL 1.4 spec says default EGL_CONTEXT_CLIENT_VERSION is GLES1, but this is not supported by Emscripten.\n      // So user must pass EGL_CONTEXT_CLIENT_VERSION == 2 to initialize EGL.\n      var glesContextVersion = 1;\n      for(;;) {\n        var param = HEAP32[((contextAttribs)>>2)];\n        if (param == 0x3098 /*EGL_CONTEXT_CLIENT_VERSION*/) {\n          glesContextVersion = HEAP32[(((contextAttribs)+(4))>>2)];\n        } else if (param == 0x3038 /*EGL_NONE*/) {\n          break;\n        } else {\n          /* EGL1.4 specifies only EGL_CONTEXT_CLIENT_VERSION as supported attribute */\n          EGL.setErrorCode(0x3004 /*EGL_BAD_ATTRIBUTE*/);\n          return 0;\n        }\n        contextAttribs += 8;\n      }\n      if (glesContextVersion != 2) {\n        EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */);\n        return 0; /* EGL_NO_CONTEXT */\n      }\n  \n      EGL.contextAttributes.majorVersion = glesContextVersion - 1; // WebGL 1 is GLES 2, WebGL2 is GLES3\n      EGL.contextAttributes.minorVersion = 0;\n  \n      EGL.context = GL.createContext(Module['canvas'], EGL.contextAttributes);\n  \n      if (EGL.context != 0) {\n        EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n  \n        // Run callbacks so that GL emulation works\n        GL.makeContextCurrent(EGL.context);\n        Module.useWebGL = true;\n        Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() });\n  \n        // Note: This function only creates a context, but it shall not make it active.\n        GL.makeContextCurrent(null);\n        return 62004; // Magic ID for Emscripten EGLContext\n      } else {\n        EGL.setErrorCode(0x3009 /* EGL_BAD_MATCH */); // By the EGL 1.4 spec, an implementation that does not support GLES2 (WebGL in this case), this error code is set.\n        return 0; /* EGL_NO_CONTEXT */\n      }\n    }\n\n  function _eglCreateWindowSurface(display, config, win, attrib_list) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n      if (config != 62002 /* Magic ID for the only EGLConfig supported by Emscripten */) {\n        EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */);\n        return 0;\n      }\n      // TODO: Examine attrib_list! Parameters that can be present there are:\n      // - EGL_RENDER_BUFFER (must be EGL_BACK_BUFFER)\n      // - EGL_VG_COLORSPACE (can't be set)\n      // - EGL_VG_ALPHA_FORMAT (can't be set)\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      return 62006; /* Magic ID for Emscripten 'default surface' */\n    }\n\n  function _eglDestroyContext(display, context) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n      if (context != 62004 /* Magic ID for Emscripten EGLContext */) {\n        EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */);\n        return 0;\n      }\n  \n      GL.deleteContext(EGL.context);\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      if (EGL.currentContext == context) {\n        EGL.currentContext = 0;\n      }\n      return 1 /* EGL_TRUE */;\n    }\n\n  function _eglDestroySurface(display, surface) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n      if (surface != 62006 /* Magic ID for the only EGLSurface supported by Emscripten */) {\n        EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */);\n        return 1;\n      }\n      if (EGL.currentReadSurface == surface) {\n        EGL.currentReadSurface = 0;\n      }\n      if (EGL.currentDrawSurface == surface) {\n        EGL.currentDrawSurface = 0;\n      }\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      return 1; /* Magic ID for Emscripten 'default surface' */\n    }\n\n  function _eglGetConfigAttrib(display, config, attribute, value) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n      if (config != 62002 /* Magic ID for the only EGLConfig supported by Emscripten */) {\n        EGL.setErrorCode(0x3005 /* EGL_BAD_CONFIG */);\n        return 0;\n      }\n      if (!value) {\n        EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);\n        return 0;\n      }\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      switch(attribute) {\n      case 0x3020: // EGL_BUFFER_SIZE\n        HEAP32[((value)>>2)] = EGL.contextAttributes.alpha ? 32 : 24;\n        return 1;\n      case 0x3021: // EGL_ALPHA_SIZE\n        HEAP32[((value)>>2)] = EGL.contextAttributes.alpha ? 8 : 0;\n        return 1;\n      case 0x3022: // EGL_BLUE_SIZE\n        HEAP32[((value)>>2)] = 8;\n        return 1;\n      case 0x3023: // EGL_GREEN_SIZE\n        HEAP32[((value)>>2)] = 8;\n        return 1;\n      case 0x3024: // EGL_RED_SIZE\n        HEAP32[((value)>>2)] = 8;\n        return 1;\n      case 0x3025: // EGL_DEPTH_SIZE\n        HEAP32[((value)>>2)] = EGL.contextAttributes.depth ? 24 : 0;\n        return 1;\n      case 0x3026: // EGL_STENCIL_SIZE\n        HEAP32[((value)>>2)] = EGL.contextAttributes.stencil ? 8 : 0;\n        return 1;\n      case 0x3027: // EGL_CONFIG_CAVEAT\n        // We can return here one of EGL_NONE (0x3038), EGL_SLOW_CONFIG (0x3050) or EGL_NON_CONFORMANT_CONFIG (0x3051).\n        HEAP32[((value)>>2)] = 0x3038;\n        return 1;\n      case 0x3028: // EGL_CONFIG_ID\n        HEAP32[((value)>>2)] = 62002;\n        return 1;\n      case 0x3029: // EGL_LEVEL\n        HEAP32[((value)>>2)] = 0;\n        return 1;\n      case 0x302A: // EGL_MAX_PBUFFER_HEIGHT\n        HEAP32[((value)>>2)] = 4096;\n        return 1;\n      case 0x302B: // EGL_MAX_PBUFFER_PIXELS\n        HEAP32[((value)>>2)] = 16777216;\n        return 1;\n      case 0x302C: // EGL_MAX_PBUFFER_WIDTH\n        HEAP32[((value)>>2)] = 4096;\n        return 1;\n      case 0x302D: // EGL_NATIVE_RENDERABLE\n        HEAP32[((value)>>2)] = 0;\n        return 1;\n      case 0x302E: // EGL_NATIVE_VISUAL_ID\n        HEAP32[((value)>>2)] = 0;\n        return 1;\n      case 0x302F: // EGL_NATIVE_VISUAL_TYPE\n        HEAP32[((value)>>2)] = 0x3038;\n        return 1;\n      case 0x3031: // EGL_SAMPLES\n        HEAP32[((value)>>2)] = EGL.contextAttributes.antialias ? 4 : 0;\n        return 1;\n      case 0x3032: // EGL_SAMPLE_BUFFERS\n        HEAP32[((value)>>2)] = EGL.contextAttributes.antialias ? 1 : 0;\n        return 1;\n      case 0x3033: // EGL_SURFACE_TYPE\n        HEAP32[((value)>>2)] = 0x4;\n        return 1;\n      case 0x3034: // EGL_TRANSPARENT_TYPE\n        // If this returns EGL_TRANSPARENT_RGB (0x3052), transparency is used through color-keying. No such thing applies to Emscripten canvas.\n        HEAP32[((value)>>2)] = 0x3038;\n        return 1;\n      case 0x3035: // EGL_TRANSPARENT_BLUE_VALUE\n      case 0x3036: // EGL_TRANSPARENT_GREEN_VALUE\n      case 0x3037: // EGL_TRANSPARENT_RED_VALUE\n        // \"If EGL_TRANSPARENT_TYPE is EGL_NONE, then the values for EGL_TRANSPARENT_RED_VALUE, EGL_TRANSPARENT_GREEN_VALUE, and EGL_TRANSPARENT_BLUE_VALUE are undefined.\"\n        HEAP32[((value)>>2)] = -1;\n        return 1;\n      case 0x3039: // EGL_BIND_TO_TEXTURE_RGB\n      case 0x303A: // EGL_BIND_TO_TEXTURE_RGBA\n        HEAP32[((value)>>2)] = 0;\n        return 1;\n      case 0x303B: // EGL_MIN_SWAP_INTERVAL\n        HEAP32[((value)>>2)] = 0;\n        return 1;\n      case 0x303C: // EGL_MAX_SWAP_INTERVAL\n        HEAP32[((value)>>2)] = 1;\n        return 1;\n      case 0x303D: // EGL_LUMINANCE_SIZE\n      case 0x303E: // EGL_ALPHA_MASK_SIZE\n        HEAP32[((value)>>2)] = 0;\n        return 1;\n      case 0x303F: // EGL_COLOR_BUFFER_TYPE\n        // EGL has two types of buffers: EGL_RGB_BUFFER and EGL_LUMINANCE_BUFFER.\n        HEAP32[((value)>>2)] = 0x308E;\n        return 1;\n      case 0x3040: // EGL_RENDERABLE_TYPE\n        // A bit combination of EGL_OPENGL_ES_BIT,EGL_OPENVG_BIT,EGL_OPENGL_ES2_BIT and EGL_OPENGL_BIT.\n        HEAP32[((value)>>2)] = 0x4;\n        return 1;\n      case 0x3042: // EGL_CONFORMANT\n        // \"EGL_CONFORMANT is a mask indicating if a client API context created with respect to the corresponding EGLConfig will pass the required conformance tests for that API.\"\n        HEAP32[((value)>>2)] = 0;\n        return 1;\n      default:\n        EGL.setErrorCode(0x3004 /* EGL_BAD_ATTRIBUTE */);\n        return 0;\n      }\n    }\n\n  function _eglGetDisplay(nativeDisplayType) {\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      // Note: As a 'conformant' implementation of EGL, we would prefer to init here only if the user\n      //       calls this function with EGL_DEFAULT_DISPLAY. Other display IDs would be preferred to be unsupported\n      //       and EGL_NO_DISPLAY returned. Uncomment the following code lines to do this.\n      // Instead, an alternative route has been preferred, namely that the Emscripten EGL implementation\n      // \"emulates\" X11, and eglGetDisplay is expected to accept/receive a pointer to an X11 Display object.\n      // Therefore, be lax and allow anything to be passed in, and return the magic handle to our default EGLDisplay object.\n  \n  //    if (nativeDisplayType == 0 /* EGL_DEFAULT_DISPLAY */) {\n          return 62000; // Magic ID for Emscripten 'default display'\n  //    }\n  //    else\n  //      return 0; // EGL_NO_DISPLAY\n    }\n\n  function _eglGetError() {\n      return EGL.errorCode;\n    }\n\n  function _eglInitialize(display, majorVersion, minorVersion) {\n      if (display == 62000 /* Magic ID for Emscripten 'default display' */) {\n        if (majorVersion) {\n          HEAP32[((majorVersion)>>2)] = 1; // Advertise EGL Major version: '1'\n        }\n        if (minorVersion) {\n          HEAP32[((minorVersion)>>2)] = 4; // Advertise EGL Minor version: '4'\n        }\n        EGL.defaultDisplayInitialized = true;\n        EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n        return 1;\n      }\n      else {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n    }\n\n  function _eglMakeCurrent(display, draw, read, context) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0 /* EGL_FALSE */;\n      }\n      //\\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy.\n      if (context != 0 && context != 62004 /* Magic ID for Emscripten EGLContext */) {\n        EGL.setErrorCode(0x3006 /* EGL_BAD_CONTEXT */);\n        return 0;\n      }\n      if ((read != 0 && read != 62006) || (draw != 0 && draw != 62006 /* Magic ID for Emscripten 'default surface' */)) {\n        EGL.setErrorCode(0x300D /* EGL_BAD_SURFACE */);\n        return 0;\n      }\n  \n      GL.makeContextCurrent(context ? EGL.context : null);\n  \n      EGL.currentContext = context;\n      EGL.currentDrawSurface = draw;\n      EGL.currentReadSurface = read;\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      return 1 /* EGL_TRUE */;\n    }\n\n  function _eglQueryString(display, name) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n      //\\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy.\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      if (EGL.stringCache[name]) return EGL.stringCache[name];\n      var ret;\n      switch(name) {\n        case 0x3053 /* EGL_VENDOR */: ret = allocateUTF8(\"Emscripten\"); break;\n        case 0x3054 /* EGL_VERSION */: ret = allocateUTF8(\"1.4 Emscripten EGL\"); break;\n        case 0x3055 /* EGL_EXTENSIONS */:  ret = allocateUTF8(\"\"); break; // Currently not supporting any EGL extensions.\n        case 0x308D /* EGL_CLIENT_APIS */: ret = allocateUTF8(\"OpenGL_ES\"); break;\n        default:\n          EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */);\n          return 0;\n      }\n      EGL.stringCache[name] = ret;\n      return ret;\n    }\n\n  function _eglSwapBuffers() {\n  \n      if (!EGL.defaultDisplayInitialized) {\n        EGL.setErrorCode(0x3001 /* EGL_NOT_INITIALIZED */);\n      } else if (!Module.ctx) {\n        EGL.setErrorCode(0x3002 /* EGL_BAD_ACCESS */);\n      } else if (Module.ctx.isContextLost()) {\n        EGL.setErrorCode(0x300E /* EGL_CONTEXT_LOST */);\n      } else {\n        // According to documentation this does an implicit flush.\n        // Due to discussion at https://github.com/emscripten-core/emscripten/pull/1871\n        // the flush was removed since this _may_ result in slowing code down.\n        //_glFlush();\n        EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n        return 1 /* EGL_TRUE */;\n      }\n      return 0 /* EGL_FALSE */;\n    }\n\n  function _eglSwapInterval(display, interval) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n      if (interval == 0) _emscripten_set_main_loop_timing(0/*EM_TIMING_SETTIMEOUT*/, 0);\n      else _emscripten_set_main_loop_timing(1/*EM_TIMING_RAF*/, interval);\n  \n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      return 1;\n    }\n\n  function _eglTerminate(display) {\n      if (display != 62000 /* Magic ID for Emscripten 'default display' */) {\n        EGL.setErrorCode(0x3008 /* EGL_BAD_DISPLAY */);\n        return 0;\n      }\n      EGL.currentContext = 0;\n      EGL.currentReadSurface = 0;\n      EGL.currentDrawSurface = 0;\n      EGL.defaultDisplayInitialized = false;\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      return 1;\n    }\n\n  function _eglWaitClient() {\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      return 1;\n    }\n  function _eglWaitGL(\n  ) {\n  return _eglWaitClient();\n  }\n\n  function _eglWaitNative(nativeEngineId) {\n      EGL.setErrorCode(0x3000 /* EGL_SUCCESS */);\n      return 1;\n    }\n\n  function _emscripten_asm_const_int(code, sigPtr, argbuf) {\n      var args = readAsmConstArgs(sigPtr, argbuf);\n      return ASM_CONSTS[code].apply(null, args);\n    }\n\n  function _emscripten_async_wget(url, file, onload, onerror) {\n      noExitRuntime = true;\n  \n      var _url = UTF8ToString(url);\n      var _file = UTF8ToString(file);\n      _file = PATH_FS.resolve(_file);\n      function doCallback(callback) {\n        if (callback) {\n          var stack = stackSave();\n          wasmTable.get(callback)(allocate(intArrayFromString(_file), ALLOC_STACK));\n          stackRestore(stack);\n        }\n      }\n      var destinationDirectory = PATH.dirname(_file);\n      FS.createPreloadedFile(\n        destinationDirectory,\n        PATH.basename(_file),\n        _url, true, true,\n        function() {\n          doCallback(onload);\n        },\n        function() {\n          doCallback(onerror);\n        },\n        false, // dontCreateFile\n        false, // canOwn\n        function() { // preFinish\n          // if a file exists there, we overwrite it\n          try {\n            FS.unlink(_file);\n          } catch (e) {}\n          // if the destination directory does not yet exist, create it\n          FS.mkdirTree(destinationDirectory);\n        }\n      );\n    }\n\n  var JSEvents={inEventHandler:0,removeAllEventListeners:function() {\n        for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) {\n          JSEvents._removeHandler(i);\n        }\n        JSEvents.eventHandlers = [];\n        JSEvents.deferredCalls = [];\n      },registerRemoveEventListeners:function() {\n        if (!JSEvents.removeEventListenersRegistered) {\n          __ATEXIT__.push(JSEvents.removeAllEventListeners);\n          JSEvents.removeEventListenersRegistered = true;\n        }\n      },deferredCalls:[],deferCall:function(targetFunction, precedence, argsList) {\n        function arraysHaveEqualContent(arrA, arrB) {\n          if (arrA.length != arrB.length) return false;\n  \n          for(var i in arrA) {\n            if (arrA[i] != arrB[i]) return false;\n          }\n          return true;\n        }\n        // Test if the given call was already queued, and if so, don't add it again.\n        for(var i in JSEvents.deferredCalls) {\n          var call = JSEvents.deferredCalls[i];\n          if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) {\n            return;\n          }\n        }\n        JSEvents.deferredCalls.push({\n          targetFunction: targetFunction,\n          precedence: precedence,\n          argsList: argsList\n        });\n  \n        JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; });\n      },removeDeferredCalls:function(targetFunction) {\n        for(var i = 0; i < JSEvents.deferredCalls.length; ++i) {\n          if (JSEvents.deferredCalls[i].targetFunction == targetFunction) {\n            JSEvents.deferredCalls.splice(i, 1);\n            --i;\n          }\n        }\n      },canPerformEventHandlerRequests:function() {\n        return JSEvents.inEventHandler && JSEvents.currentEventHandler.allowsDeferredCalls;\n      },runDeferredCalls:function() {\n        if (!JSEvents.canPerformEventHandlerRequests()) {\n          return;\n        }\n        for(var i = 0; i < JSEvents.deferredCalls.length; ++i) {\n          var call = JSEvents.deferredCalls[i];\n          JSEvents.deferredCalls.splice(i, 1);\n          --i;\n          call.targetFunction.apply(null, call.argsList);\n        }\n      },eventHandlers:[],removeAllHandlersOnTarget:function(target, eventTypeString) {\n        for(var i = 0; i < JSEvents.eventHandlers.length; ++i) {\n          if (JSEvents.eventHandlers[i].target == target && \n            (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) {\n             JSEvents._removeHandler(i--);\n           }\n        }\n      },_removeHandler:function(i) {\n        var h = JSEvents.eventHandlers[i];\n        h.target.removeEventListener(h.eventTypeString, h.eventListenerFunc, h.useCapture);\n        JSEvents.eventHandlers.splice(i, 1);\n      },registerOrRemoveHandler:function(eventHandler) {\n        var jsEventHandler = function jsEventHandler(event) {\n          // Increment nesting count for the event handler.\n          ++JSEvents.inEventHandler;\n          JSEvents.currentEventHandler = eventHandler;\n          // Process any old deferred calls the user has placed.\n          JSEvents.runDeferredCalls();\n          // Process the actual event, calls back to user C code handler.\n          eventHandler.handlerFunc(event);\n          // Process any new deferred calls that were placed right now from this event handler.\n          JSEvents.runDeferredCalls();\n          // Out of event handler - restore nesting count.\n          --JSEvents.inEventHandler;\n        };\n        \n        if (eventHandler.callbackfunc) {\n          eventHandler.eventListenerFunc = jsEventHandler;\n          eventHandler.target.addEventListener(eventHandler.eventTypeString, jsEventHandler, eventHandler.useCapture);\n          JSEvents.eventHandlers.push(eventHandler);\n          JSEvents.registerRemoveEventListeners();\n        } else {\n          for(var i = 0; i < JSEvents.eventHandlers.length; ++i) {\n            if (JSEvents.eventHandlers[i].target == eventHandler.target\n             && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) {\n               JSEvents._removeHandler(i--);\n             }\n          }\n        }\n      },getNodeNameForTarget:function(target) {\n        if (!target) return '';\n        if (target == window) return '#window';\n        if (target == screen) return '#screen';\n        return (target && target.nodeName) ? target.nodeName : '';\n      },fullscreenEnabled:function() {\n        return document.fullscreenEnabled\n        // Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitFullscreenEnabled.\n        // TODO: If Safari at some point ships with unprefixed version, update the version check above.\n        || document.webkitFullscreenEnabled\n         ;\n      }};\n  \n  var currentFullscreenStrategy={};\n  \n  function maybeCStringToJsString(cString) {\n      // \"cString > 2\" checks if the input is a number, and isn't of the special\n      // values we accept here, EMSCRIPTEN_EVENT_TARGET_* (which map to 0, 1, 2).\n      // In other words, if cString > 2 then it's a pointer to a valid place in\n      // memory, and points to a C string.\n      return cString > 2 ? UTF8ToString(cString) : cString;\n    }\n  \n  var specialHTMLTargets=[0, typeof document !== 'undefined' ? document : 0, typeof window !== 'undefined' ? window : 0];\n  function findEventTarget(target) {\n      target = maybeCStringToJsString(target);\n      var domElement = specialHTMLTargets[target] || (typeof document !== 'undefined' ? document.querySelector(target) : undefined);\n      return domElement;\n    }\n  function findCanvasEventTarget(target) { return findEventTarget(target); }\n  function _emscripten_get_canvas_element_size(target, width, height) {\n      var canvas = findCanvasEventTarget(target);\n      if (!canvas) return -4;\n      HEAP32[((width)>>2)] = canvas.width;\n      HEAP32[((height)>>2)] = canvas.height;\n    }\n  function getCanvasElementSize(target) {\n      var stackTop = stackSave();\n      var w = stackAlloc(8);\n      var h = w + 4;\n  \n      var targetInt = stackAlloc(target.id.length+1);\n      stringToUTF8(target.id, targetInt, target.id.length+1);\n      var ret = _emscripten_get_canvas_element_size(targetInt, w, h);\n      var size = [HEAP32[((w)>>2)], HEAP32[((h)>>2)]];\n      stackRestore(stackTop);\n      return size;\n    }\n  \n  function _emscripten_set_canvas_element_size(target, width, height) {\n      var canvas = findCanvasEventTarget(target);\n      if (!canvas) return -4;\n      canvas.width = width;\n      canvas.height = height;\n      return 0;\n    }\n  function setCanvasElementSize(target, width, height) {\n      if (!target.controlTransferredOffscreen) {\n        target.width = width;\n        target.height = height;\n      } else {\n        // This function is being called from high-level JavaScript code instead of asm.js/Wasm,\n        // and it needs to synchronously proxy over to another thread, so marshal the string onto the heap to do the call.\n        var stackTop = stackSave();\n        var targetInt = stackAlloc(target.id.length+1);\n        stringToUTF8(target.id, targetInt, target.id.length+1);\n        _emscripten_set_canvas_element_size(targetInt, width, height);\n        stackRestore(stackTop);\n      }\n    }\n  function registerRestoreOldStyle(canvas) {\n      var canvasSize = getCanvasElementSize(canvas);\n      var oldWidth = canvasSize[0];\n      var oldHeight = canvasSize[1];\n      var oldCssWidth = canvas.style.width;\n      var oldCssHeight = canvas.style.height;\n      var oldBackgroundColor = canvas.style.backgroundColor; // Chrome reads color from here.\n      var oldDocumentBackgroundColor = document.body.style.backgroundColor; // IE11 reads color from here.\n      // Firefox always has black background color.\n      var oldPaddingLeft = canvas.style.paddingLeft; // Chrome, FF, Safari\n      var oldPaddingRight = canvas.style.paddingRight;\n      var oldPaddingTop = canvas.style.paddingTop;\n      var oldPaddingBottom = canvas.style.paddingBottom;\n      var oldMarginLeft = canvas.style.marginLeft; // IE11\n      var oldMarginRight = canvas.style.marginRight;\n      var oldMarginTop = canvas.style.marginTop;\n      var oldMarginBottom = canvas.style.marginBottom;\n      var oldDocumentBodyMargin = document.body.style.margin;\n      var oldDocumentOverflow = document.documentElement.style.overflow; // Chrome, Firefox\n      var oldDocumentScroll = document.body.scroll; // IE\n      var oldImageRendering = canvas.style.imageRendering;\n  \n      function restoreOldStyle() {\n        var fullscreenElement = document.fullscreenElement\n          || document.webkitFullscreenElement\n          || document.msFullscreenElement\n          ;\n        if (!fullscreenElement) {\n          document.removeEventListener('fullscreenchange', restoreOldStyle);\n  \n          // Unprefixed Fullscreen API shipped in Chromium 71 (https://bugs.chromium.org/p/chromium/issues/detail?id=383813)\n          // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version.\n          document.removeEventListener('webkitfullscreenchange', restoreOldStyle);\n  \n          setCanvasElementSize(canvas, oldWidth, oldHeight);\n  \n          canvas.style.width = oldCssWidth;\n          canvas.style.height = oldCssHeight;\n          canvas.style.backgroundColor = oldBackgroundColor; // Chrome\n          // IE11 hack: assigning 'undefined' or an empty string to document.body.style.backgroundColor has no effect, so first assign back the default color\n          // before setting the undefined value. Setting undefined value is also important, or otherwise we would later treat that as something that the user\n          // had explicitly set so subsequent fullscreen transitions would not set background color properly.\n          if (!oldDocumentBackgroundColor) document.body.style.backgroundColor = 'white';\n          document.body.style.backgroundColor = oldDocumentBackgroundColor; // IE11\n          canvas.style.paddingLeft = oldPaddingLeft; // Chrome, FF, Safari\n          canvas.style.paddingRight = oldPaddingRight;\n          canvas.style.paddingTop = oldPaddingTop;\n          canvas.style.paddingBottom = oldPaddingBottom;\n          canvas.style.marginLeft = oldMarginLeft; // IE11\n          canvas.style.marginRight = oldMarginRight;\n          canvas.style.marginTop = oldMarginTop;\n          canvas.style.marginBottom = oldMarginBottom;\n          document.body.style.margin = oldDocumentBodyMargin;\n          document.documentElement.style.overflow = oldDocumentOverflow; // Chrome, Firefox\n          document.body.scroll = oldDocumentScroll; // IE\n          canvas.style.imageRendering = oldImageRendering;\n          if (canvas.GLctxObject) canvas.GLctxObject.GLctx.viewport(0, 0, oldWidth, oldHeight);\n  \n          if (currentFullscreenStrategy.canvasResizedCallback) {\n            wasmTable.get(currentFullscreenStrategy.canvasResizedCallback)(37, 0, currentFullscreenStrategy.canvasResizedCallbackUserData);\n          }\n        }\n      }\n      document.addEventListener('fullscreenchange', restoreOldStyle);\n      // Unprefixed Fullscreen API shipped in Chromium 71 (https://bugs.chromium.org/p/chromium/issues/detail?id=383813)\n      // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version.\n      document.addEventListener('webkitfullscreenchange', restoreOldStyle);\n      return restoreOldStyle;\n    }\n  \n  function setLetterbox(element, topBottom, leftRight) {\n        // Cannot use margin to specify letterboxes in FF or Chrome, since those ignore margins in fullscreen mode.\n        element.style.paddingLeft = element.style.paddingRight = leftRight + 'px';\n        element.style.paddingTop = element.style.paddingBottom = topBottom + 'px';\n    }\n  \n  function getBoundingClientRect(e) {\n      return specialHTMLTargets.indexOf(e) < 0 ? e.getBoundingClientRect() : {'left':0,'top':0};\n    }\n  function _JSEvents_resizeCanvasForFullscreen(target, strategy) {\n      var restoreOldStyle = registerRestoreOldStyle(target);\n      var cssWidth = strategy.softFullscreen ? innerWidth : screen.width;\n      var cssHeight = strategy.softFullscreen ? innerHeight : screen.height;\n      var rect = getBoundingClientRect(target);\n      var windowedCssWidth = rect.width;\n      var windowedCssHeight = rect.height;\n      var canvasSize = getCanvasElementSize(target);\n      var windowedRttWidth = canvasSize[0];\n      var windowedRttHeight = canvasSize[1];\n  \n      if (strategy.scaleMode == 3) {\n        setLetterbox(target, (cssHeight - windowedCssHeight) / 2, (cssWidth - windowedCssWidth) / 2);\n        cssWidth = windowedCssWidth;\n        cssHeight = windowedCssHeight;\n      } else if (strategy.scaleMode == 2) {\n        if (cssWidth*windowedRttHeight < windowedRttWidth*cssHeight) {\n          var desiredCssHeight = windowedRttHeight * cssWidth / windowedRttWidth;\n          setLetterbox(target, (cssHeight - desiredCssHeight) / 2, 0);\n          cssHeight = desiredCssHeight;\n        } else {\n          var desiredCssWidth = windowedRttWidth * cssHeight / windowedRttHeight;\n          setLetterbox(target, 0, (cssWidth - desiredCssWidth) / 2);\n          cssWidth = desiredCssWidth;\n        }\n      }\n  \n      // If we are adding padding, must choose a background color or otherwise Chrome will give the\n      // padding a default white color. Do it only if user has not customized their own background color.\n      if (!target.style.backgroundColor) target.style.backgroundColor = 'black';\n      // IE11 does the same, but requires the color to be set in the document body.\n      if (!document.body.style.backgroundColor) document.body.style.backgroundColor = 'black'; // IE11\n      // Firefox always shows black letterboxes independent of style color.\n  \n      target.style.width = cssWidth + 'px';\n      target.style.height = cssHeight + 'px';\n  \n      if (strategy.filteringMode == 1) {\n        target.style.imageRendering = 'optimizeSpeed';\n        target.style.imageRendering = '-moz-crisp-edges';\n        target.style.imageRendering = '-o-crisp-edges';\n        target.style.imageRendering = '-webkit-optimize-contrast';\n        target.style.imageRendering = 'optimize-contrast';\n        target.style.imageRendering = 'crisp-edges';\n        target.style.imageRendering = 'pixelated';\n      }\n  \n      var dpiScale = (strategy.canvasResolutionScaleMode == 2) ? devicePixelRatio : 1;\n      if (strategy.canvasResolutionScaleMode != 0) {\n        var newWidth = (cssWidth * dpiScale)|0;\n        var newHeight = (cssHeight * dpiScale)|0;\n        setCanvasElementSize(target, newWidth, newHeight);\n        if (target.GLctxObject) target.GLctxObject.GLctx.viewport(0, 0, newWidth, newHeight);\n      }\n      return restoreOldStyle;\n    }\n  function _JSEvents_requestFullscreen(target, strategy) {\n      // EMSCRIPTEN_FULLSCREEN_SCALE_DEFAULT + EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_NONE is a mode where no extra logic is performed to the DOM elements.\n      if (strategy.scaleMode != 0 || strategy.canvasResolutionScaleMode != 0) {\n        _JSEvents_resizeCanvasForFullscreen(target, strategy);\n      }\n  \n      if (target.requestFullscreen) {\n        target.requestFullscreen();\n      } else if (target.webkitRequestFullscreen) {\n        target.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);\n      } else {\n        return JSEvents.fullscreenEnabled() ? -3 : -1;\n      }\n  \n      currentFullscreenStrategy = strategy;\n  \n      if (strategy.canvasResizedCallback) {\n        wasmTable.get(strategy.canvasResizedCallback)(37, 0, strategy.canvasResizedCallbackUserData);\n      }\n  \n      return 0;\n    }\n  function _emscripten_exit_fullscreen() {\n      if (!JSEvents.fullscreenEnabled()) return -1;\n      // Make sure no queued up calls will fire after this.\n      JSEvents.removeDeferredCalls(_JSEvents_requestFullscreen);\n  \n      var d = specialHTMLTargets[1];\n      if (d.exitFullscreen) {\n        d.fullscreenElement && d.exitFullscreen();\n      } else if (d.webkitExitFullscreen) {\n        d.webkitFullscreenElement && d.webkitExitFullscreen();\n      } else {\n        return -1;\n      }\n  \n      return 0;\n    }\n\n  function requestPointerLock(target) {\n      if (target.requestPointerLock) {\n        target.requestPointerLock();\n      } else if (target.msRequestPointerLock) {\n        target.msRequestPointerLock();\n      } else {\n        // document.body is known to accept pointer lock, so use that to differentiate if the user passed a bad element,\n        // or if the whole browser just doesn't support the feature.\n        if (document.body.requestPointerLock\n          || document.body.msRequestPointerLock\n          ) {\n          return -3;\n        } else {\n          return -1;\n        }\n      }\n      return 0;\n    }\n  function _emscripten_exit_pointerlock() {\n      // Make sure no queued up calls will fire after this.\n      JSEvents.removeDeferredCalls(requestPointerLock);\n  \n      if (document.exitPointerLock) {\n        document.exitPointerLock();\n      } else if (document.msExitPointerLock) {\n        document.msExitPointerLock();\n      } else {\n        return -1;\n      }\n      return 0;\n    }\n\n  function _emscripten_get_device_pixel_ratio() {\n      return (typeof devicePixelRatio === 'number' && devicePixelRatio) || 1.0;\n    }\n\n  function _emscripten_get_element_css_size(target, width, height) {\n      target = findEventTarget(target);\n      if (!target) return -4;\n  \n      var rect = getBoundingClientRect(target);\n      HEAPF64[((width)>>3)] = rect.width;\n      HEAPF64[((height)>>3)] = rect.height;\n  \n      return 0;\n    }\n\n  function fillGamepadEventData(eventStruct, e) {\n      HEAPF64[((eventStruct)>>3)] = e.timestamp;\n      for(var i = 0; i < e.axes.length; ++i) {\n        HEAPF64[(((eventStruct+i*8)+(16))>>3)] = e.axes[i];\n      }\n      for(var i = 0; i < e.buttons.length; ++i) {\n        if (typeof(e.buttons[i]) === 'object') {\n          HEAPF64[(((eventStruct+i*8)+(528))>>3)] = e.buttons[i].value;\n        } else {\n          HEAPF64[(((eventStruct+i*8)+(528))>>3)] = e.buttons[i];\n        }\n      }\n      for(var i = 0; i < e.buttons.length; ++i) {\n        if (typeof(e.buttons[i]) === 'object') {\n          HEAP32[(((eventStruct+i*4)+(1040))>>2)] = e.buttons[i].pressed;\n        } else {\n          // Assigning a boolean to HEAP32, that's ok, but Closure would like to warn about it:\n          /** @suppress {checkTypes} */\n          HEAP32[(((eventStruct+i*4)+(1040))>>2)] = e.buttons[i] == 1;\n        }\n      }\n      HEAP32[(((eventStruct)+(1296))>>2)] = e.connected;\n      HEAP32[(((eventStruct)+(1300))>>2)] = e.index;\n      HEAP32[(((eventStruct)+(8))>>2)] = e.axes.length;\n      HEAP32[(((eventStruct)+(12))>>2)] = e.buttons.length;\n      stringToUTF8(e.id, eventStruct + 1304, 64);\n      stringToUTF8(e.mapping, eventStruct + 1368, 64);\n    }\n  function _emscripten_get_gamepad_status(index, gamepadState) {\n      if (!JSEvents.lastGamepadState) throw 'emscripten_get_gamepad_status() can only be called after having first called emscripten_sample_gamepad_data() and that function has returned EMSCRIPTEN_RESULT_SUCCESS!';\n  \n      // INVALID_PARAM is returned on a Gamepad index that never was there.\n      if (index < 0 || index >= JSEvents.lastGamepadState.length) return -5;\n  \n      // NO_DATA is returned on a Gamepad index that was removed.\n      // For previously disconnected gamepads there should be an empty slot (null/undefined/false) at the index.\n      // This is because gamepads must keep their original position in the array.\n      // For example, removing the first of two gamepads produces [null/undefined/false, gamepad].\n      if (!JSEvents.lastGamepadState[index]) return -7;\n  \n      fillGamepadEventData(gamepadState, JSEvents.lastGamepadState[index]);\n      return 0;\n    }\n\n  function _emscripten_get_num_gamepads() {\n      if (!JSEvents.lastGamepadState) throw 'emscripten_get_num_gamepads() can only be called after having first called emscripten_sample_gamepad_data() and that function has returned EMSCRIPTEN_RESULT_SUCCESS!';\n      // N.B. Do not call emscripten_get_num_gamepads() unless having first called emscripten_sample_gamepad_data(), and that has returned EMSCRIPTEN_RESULT_SUCCESS.\n      // Otherwise the following line will throw an exception.\n      return JSEvents.lastGamepadState.length;\n    }\n\n  function _emscripten_glActiveTexture(x0) { GLctx['activeTexture'](x0) }\n\n  function _emscripten_glAttachShader(program, shader) {\n      GLctx.attachShader(GL.programs[program],\n                              GL.shaders[shader]);\n    }\n\n  function _emscripten_glBeginQueryEXT(target, id) {\n      GLctx.disjointTimerQueryExt['beginQueryEXT'](target, GL.timerQueriesEXT[id]);\n    }\n\n  function _emscripten_glBindAttribLocation(program, index, name) {\n      GLctx.bindAttribLocation(GL.programs[program], index, UTF8ToString(name));\n    }\n\n  function _emscripten_glBindBuffer(target, buffer) {\n  \n      GLctx.bindBuffer(target, GL.buffers[buffer]);\n    }\n\n  function _emscripten_glBindFramebuffer(target, framebuffer) {\n  \n      GLctx.bindFramebuffer(target, GL.framebuffers[framebuffer]);\n  \n    }\n\n  function _emscripten_glBindRenderbuffer(target, renderbuffer) {\n      GLctx.bindRenderbuffer(target, GL.renderbuffers[renderbuffer]);\n    }\n\n  function _emscripten_glBindTexture(target, texture) {\n      GLctx.bindTexture(target, GL.textures[texture]);\n    }\n\n  function _emscripten_glBindVertexArrayOES(vao) {\n      GLctx['bindVertexArray'](GL.vaos[vao]);\n    }\n\n  function _emscripten_glBlendColor(x0, x1, x2, x3) { GLctx['blendColor'](x0, x1, x2, x3) }\n\n  function _emscripten_glBlendEquation(x0) { GLctx['blendEquation'](x0) }\n\n  function _emscripten_glBlendEquationSeparate(x0, x1) { GLctx['blendEquationSeparate'](x0, x1) }\n\n  function _emscripten_glBlendFunc(x0, x1) { GLctx['blendFunc'](x0, x1) }\n\n  function _emscripten_glBlendFuncSeparate(x0, x1, x2, x3) { GLctx['blendFuncSeparate'](x0, x1, x2, x3) }\n\n  function _emscripten_glBufferData(target, size, data, usage) {\n  \n        // N.b. here first form specifies a heap subarray, second form an integer size, so the ?: code here is polymorphic. It is advised to avoid\n        // randomly mixing both uses in calling code, to avoid any potential JS engine JIT issues.\n        GLctx.bufferData(target, data ? HEAPU8.subarray(data, data+size) : size, usage);\n    }\n\n  function _emscripten_glBufferSubData(target, offset, size, data) {\n      GLctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size));\n    }\n\n  function _emscripten_glCheckFramebufferStatus(x0) { return GLctx['checkFramebufferStatus'](x0) }\n\n  function _emscripten_glClear(x0) { GLctx['clear'](x0) }\n\n  function _emscripten_glClearColor(x0, x1, x2, x3) { GLctx['clearColor'](x0, x1, x2, x3) }\n\n  function _emscripten_glClearDepthf(x0) { GLctx['clearDepth'](x0) }\n\n  function _emscripten_glClearStencil(x0) { GLctx['clearStencil'](x0) }\n\n  function _emscripten_glColorMask(red, green, blue, alpha) {\n      GLctx.colorMask(!!red, !!green, !!blue, !!alpha);\n    }\n\n  function _emscripten_glCompileShader(shader) {\n      GLctx.compileShader(GL.shaders[shader]);\n    }\n\n  function _emscripten_glCompressedTexImage2D(target, level, internalFormat, width, height, border, imageSize, data) {\n      GLctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, data ? HEAPU8.subarray((data), (data+imageSize)) : null);\n    }\n\n  function _emscripten_glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data) {\n      GLctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, format, data ? HEAPU8.subarray((data), (data+imageSize)) : null);\n    }\n\n  function _emscripten_glCopyTexImage2D(x0, x1, x2, x3, x4, x5, x6, x7) { GLctx['copyTexImage2D'](x0, x1, x2, x3, x4, x5, x6, x7) }\n\n  function _emscripten_glCopyTexSubImage2D(x0, x1, x2, x3, x4, x5, x6, x7) { GLctx['copyTexSubImage2D'](x0, x1, x2, x3, x4, x5, x6, x7) }\n\n  function _emscripten_glCreateProgram() {\n      var id = GL.getNewId(GL.programs);\n      var program = GLctx.createProgram();\n      program.name = id;\n      GL.programs[id] = program;\n      return id;\n    }\n\n  function _emscripten_glCreateShader(shaderType) {\n      var id = GL.getNewId(GL.shaders);\n      GL.shaders[id] = GLctx.createShader(shaderType);\n      return id;\n    }\n\n  function _emscripten_glCullFace(x0) { GLctx['cullFace'](x0) }\n\n  function _emscripten_glDeleteBuffers(n, buffers) {\n      for (var i = 0; i < n; i++) {\n        var id = HEAP32[(((buffers)+(i*4))>>2)];\n        var buffer = GL.buffers[id];\n  \n        // From spec: \"glDeleteBuffers silently ignores 0's and names that do not\n        // correspond to existing buffer objects.\"\n        if (!buffer) continue;\n  \n        GLctx.deleteBuffer(buffer);\n        buffer.name = 0;\n        GL.buffers[id] = null;\n  \n      }\n    }\n\n  function _emscripten_glDeleteFramebuffers(n, framebuffers) {\n      for (var i = 0; i < n; ++i) {\n        var id = HEAP32[(((framebuffers)+(i*4))>>2)];\n        var framebuffer = GL.framebuffers[id];\n        if (!framebuffer) continue; // GL spec: \"glDeleteFramebuffers silently ignores 0s and names that do not correspond to existing framebuffer objects\".\n        GLctx.deleteFramebuffer(framebuffer);\n        framebuffer.name = 0;\n        GL.framebuffers[id] = null;\n      }\n    }\n\n  function _emscripten_glDeleteProgram(id) {\n      if (!id) return;\n      var program = GL.programs[id];\n      if (!program) { // glDeleteProgram actually signals an error when deleting a nonexisting object, unlike some other GL delete functions.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      GLctx.deleteProgram(program);\n      program.name = 0;\n      GL.programs[id] = null;\n      GL.programInfos[id] = null;\n    }\n\n  function _emscripten_glDeleteQueriesEXT(n, ids) {\n      for (var i = 0; i < n; i++) {\n        var id = HEAP32[(((ids)+(i*4))>>2)];\n        var query = GL.timerQueriesEXT[id];\n        if (!query) continue; // GL spec: \"unused names in ids are ignored, as is the name zero.\"\n        GLctx.disjointTimerQueryExt['deleteQueryEXT'](query);\n        GL.timerQueriesEXT[id] = null;\n      }\n    }\n\n  function _emscripten_glDeleteRenderbuffers(n, renderbuffers) {\n      for (var i = 0; i < n; i++) {\n        var id = HEAP32[(((renderbuffers)+(i*4))>>2)];\n        var renderbuffer = GL.renderbuffers[id];\n        if (!renderbuffer) continue; // GL spec: \"glDeleteRenderbuffers silently ignores 0s and names that do not correspond to existing renderbuffer objects\".\n        GLctx.deleteRenderbuffer(renderbuffer);\n        renderbuffer.name = 0;\n        GL.renderbuffers[id] = null;\n      }\n    }\n\n  function _emscripten_glDeleteShader(id) {\n      if (!id) return;\n      var shader = GL.shaders[id];\n      if (!shader) { // glDeleteShader actually signals an error when deleting a nonexisting object, unlike some other GL delete functions.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      GLctx.deleteShader(shader);\n      GL.shaders[id] = null;\n    }\n\n  function _emscripten_glDeleteTextures(n, textures) {\n      for (var i = 0; i < n; i++) {\n        var id = HEAP32[(((textures)+(i*4))>>2)];\n        var texture = GL.textures[id];\n        if (!texture) continue; // GL spec: \"glDeleteTextures silently ignores 0s and names that do not correspond to existing textures\".\n        GLctx.deleteTexture(texture);\n        texture.name = 0;\n        GL.textures[id] = null;\n      }\n    }\n\n  function _emscripten_glDeleteVertexArraysOES(n, vaos) {\n      for (var i = 0; i < n; i++) {\n        var id = HEAP32[(((vaos)+(i*4))>>2)];\n        GLctx['deleteVertexArray'](GL.vaos[id]);\n        GL.vaos[id] = null;\n      }\n    }\n\n  function _emscripten_glDepthFunc(x0) { GLctx['depthFunc'](x0) }\n\n  function _emscripten_glDepthMask(flag) {\n      GLctx.depthMask(!!flag);\n    }\n\n  function _emscripten_glDepthRangef(x0, x1) { GLctx['depthRange'](x0, x1) }\n\n  function _emscripten_glDetachShader(program, shader) {\n      GLctx.detachShader(GL.programs[program],\n                              GL.shaders[shader]);\n    }\n\n  function _emscripten_glDisable(x0) { GLctx['disable'](x0) }\n\n  function _emscripten_glDisableVertexAttribArray(index) {\n      GLctx.disableVertexAttribArray(index);\n    }\n\n  function _emscripten_glDrawArrays(mode, first, count) {\n  \n      GLctx.drawArrays(mode, first, count);\n  \n    }\n\n  function _emscripten_glDrawArraysInstancedANGLE(mode, first, count, primcount) {\n      GLctx['drawArraysInstanced'](mode, first, count, primcount);\n    }\n\n  var tempFixedLengthArray=[];\n  function _emscripten_glDrawBuffersWEBGL(n, bufs) {\n  \n      var bufArray = tempFixedLengthArray[n];\n      for (var i = 0; i < n; i++) {\n        bufArray[i] = HEAP32[(((bufs)+(i*4))>>2)];\n      }\n  \n      GLctx['drawBuffers'](bufArray);\n    }\n\n  function _emscripten_glDrawElements(mode, count, type, indices) {\n  \n      GLctx.drawElements(mode, count, type, indices);\n  \n    }\n\n  function _emscripten_glDrawElementsInstancedANGLE(mode, count, type, indices, primcount) {\n      GLctx['drawElementsInstanced'](mode, count, type, indices, primcount);\n    }\n\n  function _emscripten_glEnable(x0) { GLctx['enable'](x0) }\n\n  function _emscripten_glEnableVertexAttribArray(index) {\n      GLctx.enableVertexAttribArray(index);\n    }\n\n  function _emscripten_glEndQueryEXT(target) {\n      GLctx.disjointTimerQueryExt['endQueryEXT'](target);\n    }\n\n  function _emscripten_glFinish() { GLctx['finish']() }\n\n  function _emscripten_glFlush() { GLctx['flush']() }\n\n  function _emscripten_glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer) {\n      GLctx.framebufferRenderbuffer(target, attachment, renderbuffertarget,\n                                         GL.renderbuffers[renderbuffer]);\n    }\n\n  function _emscripten_glFramebufferTexture2D(target, attachment, textarget, texture, level) {\n      GLctx.framebufferTexture2D(target, attachment, textarget,\n                                      GL.textures[texture], level);\n    }\n\n  function _emscripten_glFrontFace(x0) { GLctx['frontFace'](x0) }\n\n  function __glGenObject(n, buffers, createFunction, objectTable\n      ) {\n      for (var i = 0; i < n; i++) {\n        var buffer = GLctx[createFunction]();\n        var id = buffer && GL.getNewId(objectTable);\n        if (buffer) {\n          buffer.name = id;\n          objectTable[id] = buffer;\n        } else {\n          GL.recordError(0x502 /* GL_INVALID_OPERATION */);\n        }\n        HEAP32[(((buffers)+(i*4))>>2)] = id;\n      }\n    }\n  function _emscripten_glGenBuffers(n, buffers) {\n      __glGenObject(n, buffers, 'createBuffer', GL.buffers\n        );\n    }\n\n  function _emscripten_glGenFramebuffers(n, ids) {\n      __glGenObject(n, ids, 'createFramebuffer', GL.framebuffers\n        );\n    }\n\n  function _emscripten_glGenQueriesEXT(n, ids) {\n      for (var i = 0; i < n; i++) {\n        var query = GLctx.disjointTimerQueryExt['createQueryEXT']();\n        if (!query) {\n          GL.recordError(0x502 /* GL_INVALID_OPERATION */);\n          while(i < n) HEAP32[(((ids)+(i++*4))>>2)] = 0;\n          return;\n        }\n        var id = GL.getNewId(GL.timerQueriesEXT);\n        query.name = id;\n        GL.timerQueriesEXT[id] = query;\n        HEAP32[(((ids)+(i*4))>>2)] = id;\n      }\n    }\n\n  function _emscripten_glGenRenderbuffers(n, renderbuffers) {\n      __glGenObject(n, renderbuffers, 'createRenderbuffer', GL.renderbuffers\n        );\n    }\n\n  function _emscripten_glGenTextures(n, textures) {\n      __glGenObject(n, textures, 'createTexture', GL.textures\n        );\n    }\n\n  function _emscripten_glGenVertexArraysOES(n, arrays) {\n      __glGenObject(n, arrays, 'createVertexArray', GL.vaos\n        );\n    }\n\n  function _emscripten_glGenerateMipmap(x0) { GLctx['generateMipmap'](x0) }\n\n  function __glGetActiveAttribOrUniform(funcName, program, index, bufSize, length, size, type, name) {\n      program = GL.programs[program];\n      var info = GLctx[funcName](program, index);\n      if (info) { // If an error occurs, nothing will be written to length, size and type and name.\n        var numBytesWrittenExclNull = name && stringToUTF8(info.name, name, bufSize);\n        if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull;\n        if (size) HEAP32[((size)>>2)] = info.size;\n        if (type) HEAP32[((type)>>2)] = info.type;\n      }\n    }\n  function _emscripten_glGetActiveAttrib(program, index, bufSize, length, size, type, name) {\n      __glGetActiveAttribOrUniform('getActiveAttrib', program, index, bufSize, length, size, type, name);\n    }\n\n  function _emscripten_glGetActiveUniform(program, index, bufSize, length, size, type, name) {\n      __glGetActiveAttribOrUniform('getActiveUniform', program, index, bufSize, length, size, type, name);\n    }\n\n  function _emscripten_glGetAttachedShaders(program, maxCount, count, shaders) {\n      var result = GLctx.getAttachedShaders(GL.programs[program]);\n      var len = result.length;\n      if (len > maxCount) {\n        len = maxCount;\n      }\n      HEAP32[((count)>>2)] = len;\n      for (var i = 0; i < len; ++i) {\n        var id = GL.shaders.indexOf(result[i]);\n        HEAP32[(((shaders)+(i*4))>>2)] = id;\n      }\n    }\n\n  function _emscripten_glGetAttribLocation(program, name) {\n      return GLctx.getAttribLocation(GL.programs[program], UTF8ToString(name));\n    }\n\n  function readI53FromI64(ptr) {\n      return HEAPU32[ptr>>2] + HEAP32[ptr+4>>2] * 4294967296;\n    }\n  \n  function readI53FromU64(ptr) {\n      return HEAPU32[ptr>>2] + HEAPU32[ptr+4>>2] * 4294967296;\n    }\n  function writeI53ToI64(ptr, num) {\n      HEAPU32[ptr>>2] = num;\n      HEAPU32[ptr+4>>2] = (num - HEAPU32[ptr>>2])/4294967296;\n      var deserialized = (num >= 0) ? readI53FromU64(ptr) : readI53FromI64(ptr);\n      if (deserialized != num) warnOnce('writeI53ToI64() out of range: serialized JS Number ' + num + ' to Wasm heap as bytes lo=0x' + HEAPU32[ptr>>2].toString(16) + ', hi=0x' + HEAPU32[ptr+4>>2].toString(16) + ', which deserializes back to ' + deserialized + ' instead!');\n    }\n  function emscriptenWebGLGet(name_, p, type) {\n      // Guard against user passing a null pointer.\n      // Note that GLES2 spec does not say anything about how passing a null pointer should be treated.\n      // Testing on desktop core GL 3, the application crashes on glGetIntegerv to a null pointer, but\n      // better to report an error instead of doing anything random.\n      if (!p) {\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      var ret = undefined;\n      switch(name_) { // Handle a few trivial GLES values\n        case 0x8DFA: // GL_SHADER_COMPILER\n          ret = 1;\n          break;\n        case 0x8DF8: // GL_SHADER_BINARY_FORMATS\n          if (type != 0 && type != 1) {\n            GL.recordError(0x500); // GL_INVALID_ENUM\n          }\n          return; // Do not write anything to the out pointer, since no binary formats are supported.\n        case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS\n          ret = 0;\n          break;\n        case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS\n          // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length),\n          // so implement it ourselves to allow C++ GLES2 code get the length.\n          var formats = GLctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/);\n          ret = formats ? formats.length : 0;\n          break;\n      }\n  \n      if (ret === undefined) {\n        var result = GLctx.getParameter(name_);\n        switch (typeof(result)) {\n          case \"number\":\n            ret = result;\n            break;\n          case \"boolean\":\n            ret = result ? 1 : 0;\n            break;\n          case \"string\":\n            GL.recordError(0x500); // GL_INVALID_ENUM\n            return;\n          case \"object\":\n            if (result === null) {\n              // null is a valid result for some (e.g., which buffer is bound - perhaps nothing is bound), but otherwise\n              // can mean an invalid name_, which we need to report as an error\n              switch(name_) {\n                case 0x8894: // ARRAY_BUFFER_BINDING\n                case 0x8B8D: // CURRENT_PROGRAM\n                case 0x8895: // ELEMENT_ARRAY_BUFFER_BINDING\n                case 0x8CA6: // FRAMEBUFFER_BINDING or DRAW_FRAMEBUFFER_BINDING\n                case 0x8CA7: // RENDERBUFFER_BINDING\n                case 0x8069: // TEXTURE_BINDING_2D\n                case 0x85B5: // WebGL 2 GL_VERTEX_ARRAY_BINDING, or WebGL 1 extension OES_vertex_array_object GL_VERTEX_ARRAY_BINDING_OES\n                case 0x8514: { // TEXTURE_BINDING_CUBE_MAP\n                  ret = 0;\n                  break;\n                }\n                default: {\n                  GL.recordError(0x500); // GL_INVALID_ENUM\n                  return;\n                }\n              }\n            } else if (result instanceof Float32Array ||\n                       result instanceof Uint32Array ||\n                       result instanceof Int32Array ||\n                       result instanceof Array) {\n              for (var i = 0; i < result.length; ++i) {\n                switch (type) {\n                  case 0: HEAP32[(((p)+(i*4))>>2)] = result[i]; break;\n                  case 2: HEAPF32[(((p)+(i*4))>>2)] = result[i]; break;\n                  case 4: HEAP8[(((p)+(i))>>0)] = result[i] ? 1 : 0; break;\n                }\n              }\n              return;\n            } else {\n              try {\n                ret = result.name | 0;\n              } catch(e) {\n                GL.recordError(0x500); // GL_INVALID_ENUM\n                err('GL_INVALID_ENUM in glGet' + type + 'v: Unknown object returned from WebGL getParameter(' + name_ + ')! (error: ' + e + ')');\n                return;\n              }\n            }\n            break;\n          default:\n            GL.recordError(0x500); // GL_INVALID_ENUM\n            err('GL_INVALID_ENUM in glGet' + type + 'v: Native code calling glGet' + type + 'v(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');\n            return;\n        }\n      }\n  \n      switch (type) {\n        case 1: writeI53ToI64(p, ret); break;\n        case 0: HEAP32[((p)>>2)] = ret; break;\n        case 2:   HEAPF32[((p)>>2)] = ret; break;\n        case 4: HEAP8[((p)>>0)] = ret ? 1 : 0; break;\n      }\n    }\n  function _emscripten_glGetBooleanv(name_, p) {\n      emscriptenWebGLGet(name_, p, 4);\n    }\n\n  function _emscripten_glGetBufferParameteriv(target, value, data) {\n      if (!data) {\n        // GLES2 specification does not specify how to behave if data is a null pointer. Since calling this function does not make sense\n        // if data == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      HEAP32[((data)>>2)] = GLctx.getBufferParameter(target, value);\n    }\n\n  function _emscripten_glGetError() {\n      var error = GLctx.getError() || GL.lastError;\n      GL.lastError = 0/*GL_NO_ERROR*/;\n      return error;\n    }\n\n  function _emscripten_glGetFloatv(name_, p) {\n      emscriptenWebGLGet(name_, p, 2);\n    }\n\n  function _emscripten_glGetFramebufferAttachmentParameteriv(target, attachment, pname, params) {\n      var result = GLctx.getFramebufferAttachmentParameter(target, attachment, pname);\n      if (result instanceof WebGLRenderbuffer ||\n          result instanceof WebGLTexture) {\n        result = result.name | 0;\n      }\n      HEAP32[((params)>>2)] = result;\n    }\n\n  function _emscripten_glGetIntegerv(name_, p) {\n      emscriptenWebGLGet(name_, p, 0);\n    }\n\n  function _emscripten_glGetProgramInfoLog(program, maxLength, length, infoLog) {\n      var log = GLctx.getProgramInfoLog(GL.programs[program]);\n      if (log === null) log = '(unknown error)';\n      var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0;\n      if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull;\n    }\n\n  function _emscripten_glGetProgramiv(program, pname, p) {\n      if (!p) {\n        // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n  \n      if (program >= GL.counter) {\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n  \n      var ptable = GL.programInfos[program];\n      if (!ptable) {\n        GL.recordError(0x502 /* GL_INVALID_OPERATION */);\n        return;\n      }\n  \n      if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH\n        var log = GLctx.getProgramInfoLog(GL.programs[program]);\n        if (log === null) log = '(unknown error)';\n        HEAP32[((p)>>2)] = log.length + 1;\n      } else if (pname == 0x8B87 /* GL_ACTIVE_UNIFORM_MAX_LENGTH */) {\n        HEAP32[((p)>>2)] = ptable.maxUniformLength;\n      } else if (pname == 0x8B8A /* GL_ACTIVE_ATTRIBUTE_MAX_LENGTH */) {\n        if (ptable.maxAttributeLength == -1) {\n          program = GL.programs[program];\n          var numAttribs = GLctx.getProgramParameter(program, 0x8B89/*GL_ACTIVE_ATTRIBUTES*/);\n          ptable.maxAttributeLength = 0; // Spec says if there are no active attribs, 0 must be returned.\n          for (var i = 0; i < numAttribs; ++i) {\n            var activeAttrib = GLctx.getActiveAttrib(program, i);\n            ptable.maxAttributeLength = Math.max(ptable.maxAttributeLength, activeAttrib.name.length+1);\n          }\n        }\n        HEAP32[((p)>>2)] = ptable.maxAttributeLength;\n      } else if (pname == 0x8A35 /* GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */) {\n        if (ptable.maxUniformBlockNameLength == -1) {\n          program = GL.programs[program];\n          var numBlocks = GLctx.getProgramParameter(program, 0x8A36/*GL_ACTIVE_UNIFORM_BLOCKS*/);\n          ptable.maxUniformBlockNameLength = 0;\n          for (var i = 0; i < numBlocks; ++i) {\n            var activeBlockName = GLctx.getActiveUniformBlockName(program, i);\n            ptable.maxUniformBlockNameLength = Math.max(ptable.maxUniformBlockNameLength, activeBlockName.length+1);\n          }\n        }\n        HEAP32[((p)>>2)] = ptable.maxUniformBlockNameLength;\n      } else {\n        HEAP32[((p)>>2)] = GLctx.getProgramParameter(GL.programs[program], pname);\n      }\n    }\n\n  function _emscripten_glGetQueryObjecti64vEXT(id, pname, params) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      var query = GL.timerQueriesEXT[id];\n      var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname);\n      var ret;\n      if (typeof param == 'boolean') {\n        ret = param ? 1 : 0;\n      } else {\n        ret = param;\n      }\n      writeI53ToI64(params, ret);\n    }\n\n  function _emscripten_glGetQueryObjectivEXT(id, pname, params) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      var query = GL.timerQueriesEXT[id];\n      var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname);\n      var ret;\n      if (typeof param == 'boolean') {\n        ret = param ? 1 : 0;\n      } else {\n        ret = param;\n      }\n      HEAP32[((params)>>2)] = ret;\n    }\n\n  function _emscripten_glGetQueryObjectui64vEXT(id, pname, params) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      var query = GL.timerQueriesEXT[id];\n      var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname);\n      var ret;\n      if (typeof param == 'boolean') {\n        ret = param ? 1 : 0;\n      } else {\n        ret = param;\n      }\n      writeI53ToI64(params, ret);\n    }\n\n  function _emscripten_glGetQueryObjectuivEXT(id, pname, params) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      var query = GL.timerQueriesEXT[id];\n      var param = GLctx.disjointTimerQueryExt['getQueryObjectEXT'](query, pname);\n      var ret;\n      if (typeof param == 'boolean') {\n        ret = param ? 1 : 0;\n      } else {\n        ret = param;\n      }\n      HEAP32[((params)>>2)] = ret;\n    }\n\n  function _emscripten_glGetQueryivEXT(target, pname, params) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      HEAP32[((params)>>2)] = GLctx.disjointTimerQueryExt['getQueryEXT'](target, pname);\n    }\n\n  function _emscripten_glGetRenderbufferParameteriv(target, pname, params) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if params == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      HEAP32[((params)>>2)] = GLctx.getRenderbufferParameter(target, pname);\n    }\n\n  function _emscripten_glGetShaderInfoLog(shader, maxLength, length, infoLog) {\n      var log = GLctx.getShaderInfoLog(GL.shaders[shader]);\n      if (log === null) log = '(unknown error)';\n      var numBytesWrittenExclNull = (maxLength > 0 && infoLog) ? stringToUTF8(log, infoLog, maxLength) : 0;\n      if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull;\n    }\n\n  function _emscripten_glGetShaderPrecisionFormat(shaderType, precisionType, range, precision) {\n      var result = GLctx.getShaderPrecisionFormat(shaderType, precisionType);\n      HEAP32[((range)>>2)] = result.rangeMin;\n      HEAP32[(((range)+(4))>>2)] = result.rangeMax;\n      HEAP32[((precision)>>2)] = result.precision;\n    }\n\n  function _emscripten_glGetShaderSource(shader, bufSize, length, source) {\n      var result = GLctx.getShaderSource(GL.shaders[shader]);\n      if (!result) return; // If an error occurs, nothing will be written to length or source.\n      var numBytesWrittenExclNull = (bufSize > 0 && source) ? stringToUTF8(result, source, bufSize) : 0;\n      if (length) HEAP32[((length)>>2)] = numBytesWrittenExclNull;\n    }\n\n  function _emscripten_glGetShaderiv(shader, pname, p) {\n      if (!p) {\n        // GLES2 specification does not specify how to behave if p is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH\n        var log = GLctx.getShaderInfoLog(GL.shaders[shader]);\n        if (log === null) log = '(unknown error)';\n        // The GLES2 specification says that if the shader has an empty info log,\n        // a value of 0 is returned. Otherwise the log has a null char appended.\n        // (An empty string is falsey, so we can just check that instead of\n        // looking at log.length.)\n        var logLength = log ? log.length + 1 : 0;\n        HEAP32[((p)>>2)] = logLength;\n      } else if (pname == 0x8B88) { // GL_SHADER_SOURCE_LENGTH\n        var source = GLctx.getShaderSource(GL.shaders[shader]);\n        // source may be a null, or the empty string, both of which are falsey\n        // values that we report a 0 length for.\n        var sourceLength = source ? source.length + 1 : 0;\n        HEAP32[((p)>>2)] = sourceLength;\n      } else {\n        HEAP32[((p)>>2)] = GLctx.getShaderParameter(GL.shaders[shader], pname);\n      }\n    }\n\n  function stringToNewUTF8(jsString) {\n      var length = lengthBytesUTF8(jsString)+1;\n      var cString = _malloc(length);\n      stringToUTF8(jsString, cString, length);\n      return cString;\n    }\n  function _emscripten_glGetString(name_) {\n      if (GL.stringCache[name_]) return GL.stringCache[name_];\n      var ret;\n      switch(name_) {\n        case 0x1F03 /* GL_EXTENSIONS */:\n          var exts = GLctx.getSupportedExtensions() || []; // .getSupportedExtensions() can return null if context is lost, so coerce to empty array.\n          exts = exts.concat(exts.map(function(e) { return \"GL_\" + e; }));\n          ret = stringToNewUTF8(exts.join(' '));\n          break;\n        case 0x1F00 /* GL_VENDOR */:\n        case 0x1F01 /* GL_RENDERER */:\n        case 0x9245 /* UNMASKED_VENDOR_WEBGL */:\n        case 0x9246 /* UNMASKED_RENDERER_WEBGL */:\n          var s = GLctx.getParameter(name_);\n          if (!s) {\n            GL.recordError(0x500/*GL_INVALID_ENUM*/);\n          }\n          ret = stringToNewUTF8(s);\n          break;\n  \n        case 0x1F02 /* GL_VERSION */:\n          var glVersion = GLctx.getParameter(0x1F02 /*GL_VERSION*/);\n          // return GLES version string corresponding to the version of the WebGL context\n          {\n            glVersion = 'OpenGL ES 2.0 (' + glVersion + ')';\n          }\n          ret = stringToNewUTF8(glVersion);\n          break;\n        case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */:\n          var glslVersion = GLctx.getParameter(0x8B8C /*GL_SHADING_LANGUAGE_VERSION*/);\n          // extract the version number 'N.M' from the string 'WebGL GLSL ES N.M ...'\n          var ver_re = /^WebGL GLSL ES ([0-9]\\.[0-9][0-9]?)(?:$| .*)/;\n          var ver_num = glslVersion.match(ver_re);\n          if (ver_num !== null) {\n            if (ver_num[1].length == 3) ver_num[1] = ver_num[1] + '0'; // ensure minor version has 2 digits\n            glslVersion = 'OpenGL ES GLSL ES ' + ver_num[1] + ' (' + glslVersion + ')';\n          }\n          ret = stringToNewUTF8(glslVersion);\n          break;\n        default:\n          GL.recordError(0x500/*GL_INVALID_ENUM*/);\n          return 0;\n      }\n      GL.stringCache[name_] = ret;\n      return ret;\n    }\n\n  function _emscripten_glGetTexParameterfv(target, pname, params) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      HEAPF32[((params)>>2)] = GLctx.getTexParameter(target, pname);\n    }\n\n  function _emscripten_glGetTexParameteriv(target, pname, params) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if p == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      HEAP32[((params)>>2)] = GLctx.getTexParameter(target, pname);\n    }\n\n  /** @suppress {checkTypes} */\n  function jstoi_q(str) {\n      return parseInt(str);\n    }\n  function _emscripten_glGetUniformLocation(program, name) {\n      name = UTF8ToString(name);\n  \n      var arrayIndex = 0;\n      // If user passed an array accessor \"[index]\", parse the array index off the accessor.\n      if (name[name.length - 1] == ']') {\n        var leftBrace = name.lastIndexOf('[');\n        arrayIndex = name[leftBrace+1] != ']' ? jstoi_q(name.slice(leftBrace + 1)) : 0; // \"index]\", parseInt will ignore the ']' at the end; but treat \"foo[]\" as \"foo[0]\"\n        name = name.slice(0, leftBrace);\n      }\n  \n      var uniformInfo = GL.programInfos[program] && GL.programInfos[program].uniforms[name]; // returns pair [ dimension_of_uniform_array, uniform_location ]\n      if (uniformInfo && arrayIndex >= 0 && arrayIndex < uniformInfo[0]) { // Check if user asked for an out-of-bounds element, i.e. for 'vec4 colors[3];' user could ask for 'colors[10]' which should return -1.\n        return uniformInfo[1] + arrayIndex;\n      } else {\n        return -1;\n      }\n    }\n\n  /** @suppress{checkTypes} */\n  function emscriptenWebGLGetUniform(program, location, params, type) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if params == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      var data = GLctx.getUniform(GL.programs[program], GL.uniforms[location]);\n      if (typeof data == 'number' || typeof data == 'boolean') {\n        switch (type) {\n          case 0: HEAP32[((params)>>2)] = data; break;\n          case 2: HEAPF32[((params)>>2)] = data; break;\n        }\n      } else {\n        for (var i = 0; i < data.length; i++) {\n          switch (type) {\n            case 0: HEAP32[(((params)+(i*4))>>2)] = data[i]; break;\n            case 2: HEAPF32[(((params)+(i*4))>>2)] = data[i]; break;\n          }\n        }\n      }\n    }\n  function _emscripten_glGetUniformfv(program, location, params) {\n      emscriptenWebGLGetUniform(program, location, params, 2);\n    }\n\n  function _emscripten_glGetUniformiv(program, location, params) {\n      emscriptenWebGLGetUniform(program, location, params, 0);\n    }\n\n  function _emscripten_glGetVertexAttribPointerv(index, pname, pointer) {\n      if (!pointer) {\n        // GLES2 specification does not specify how to behave if pointer is a null pointer. Since calling this function does not make sense\n        // if pointer == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      HEAP32[((pointer)>>2)] = GLctx.getVertexAttribOffset(index, pname);\n    }\n\n  /** @suppress{checkTypes} */\n  function emscriptenWebGLGetVertexAttrib(index, pname, params, type) {\n      if (!params) {\n        // GLES2 specification does not specify how to behave if params is a null pointer. Since calling this function does not make sense\n        // if params == null, issue a GL error to notify user about it.\n        GL.recordError(0x501 /* GL_INVALID_VALUE */);\n        return;\n      }\n      var data = GLctx.getVertexAttrib(index, pname);\n      if (pname == 0x889F/*VERTEX_ATTRIB_ARRAY_BUFFER_BINDING*/) {\n        HEAP32[((params)>>2)] = data && data[\"name\"];\n      } else if (typeof data == 'number' || typeof data == 'boolean') {\n        switch (type) {\n          case 0: HEAP32[((params)>>2)] = data; break;\n          case 2: HEAPF32[((params)>>2)] = data; break;\n          case 5: HEAP32[((params)>>2)] = Math.fround(data); break;\n        }\n      } else {\n        for (var i = 0; i < data.length; i++) {\n          switch (type) {\n            case 0: HEAP32[(((params)+(i*4))>>2)] = data[i]; break;\n            case 2: HEAPF32[(((params)+(i*4))>>2)] = data[i]; break;\n            case 5: HEAP32[(((params)+(i*4))>>2)] = Math.fround(data[i]); break;\n          }\n        }\n      }\n    }\n  function _emscripten_glGetVertexAttribfv(index, pname, params) {\n      // N.B. This function may only be called if the vertex attribute was specified using the function glVertexAttrib*f(),\n      // otherwise the results are undefined. (GLES3 spec 6.1.12)\n      emscriptenWebGLGetVertexAttrib(index, pname, params, 2);\n    }\n\n  function _emscripten_glGetVertexAttribiv(index, pname, params) {\n      // N.B. This function may only be called if the vertex attribute was specified using the function glVertexAttrib*f(),\n      // otherwise the results are undefined. (GLES3 spec 6.1.12)\n      emscriptenWebGLGetVertexAttrib(index, pname, params, 5);\n    }\n\n  function _emscripten_glHint(x0, x1) { GLctx['hint'](x0, x1) }\n\n  function _emscripten_glIsBuffer(buffer) {\n      var b = GL.buffers[buffer];\n      if (!b) return 0;\n      return GLctx.isBuffer(b);\n    }\n\n  function _emscripten_glIsEnabled(x0) { return GLctx['isEnabled'](x0) }\n\n  function _emscripten_glIsFramebuffer(framebuffer) {\n      var fb = GL.framebuffers[framebuffer];\n      if (!fb) return 0;\n      return GLctx.isFramebuffer(fb);\n    }\n\n  function _emscripten_glIsProgram(program) {\n      program = GL.programs[program];\n      if (!program) return 0;\n      return GLctx.isProgram(program);\n    }\n\n  function _emscripten_glIsQueryEXT(id) {\n      var query = GL.timerQueriesEXT[id];\n      if (!query) return 0;\n      return GLctx.disjointTimerQueryExt['isQueryEXT'](query);\n    }\n\n  function _emscripten_glIsRenderbuffer(renderbuffer) {\n      var rb = GL.renderbuffers[renderbuffer];\n      if (!rb) return 0;\n      return GLctx.isRenderbuffer(rb);\n    }\n\n  function _emscripten_glIsShader(shader) {\n      var s = GL.shaders[shader];\n      if (!s) return 0;\n      return GLctx.isShader(s);\n    }\n\n  function _emscripten_glIsTexture(id) {\n      var texture = GL.textures[id];\n      if (!texture) return 0;\n      return GLctx.isTexture(texture);\n    }\n\n  function _emscripten_glIsVertexArrayOES(array) {\n  \n      var vao = GL.vaos[array];\n      if (!vao) return 0;\n      return GLctx['isVertexArray'](vao);\n    }\n\n  function _emscripten_glLineWidth(x0) { GLctx['lineWidth'](x0) }\n\n  function _emscripten_glLinkProgram(program) {\n      GLctx.linkProgram(GL.programs[program]);\n      GL.populateUniformTable(program);\n    }\n\n  function _emscripten_glPixelStorei(pname, param) {\n      if (pname == 0xCF5 /* GL_UNPACK_ALIGNMENT */) {\n        GL.unpackAlignment = param;\n      }\n      GLctx.pixelStorei(pname, param);\n    }\n\n  function _emscripten_glPolygonOffset(x0, x1) { GLctx['polygonOffset'](x0, x1) }\n\n  function _emscripten_glQueryCounterEXT(id, target) {\n      GLctx.disjointTimerQueryExt['queryCounterEXT'](GL.timerQueriesEXT[id], target);\n    }\n\n  function computeUnpackAlignedImageSize(width, height, sizePerPixel, alignment) {\n      function roundedToNextMultipleOf(x, y) {\n        return (x + y - 1) & -y;\n      }\n      var plainRowSize = width * sizePerPixel;\n      var alignedRowSize = roundedToNextMultipleOf(plainRowSize, alignment);\n      return height * alignedRowSize;\n    }\n  \n  function __colorChannelsInGlTextureFormat(format) {\n      // Micro-optimizations for size: map format to size by subtracting smallest enum value (0x1902) from all values first.\n      // Also omit the most common size value (1) from the list, which is assumed by formats not on the list.\n      var colorChannels = {\n        // 0x1902 /* GL_DEPTH_COMPONENT */ - 0x1902: 1,\n        // 0x1906 /* GL_ALPHA */ - 0x1902: 1,\n        5: 3,\n        6: 4,\n        // 0x1909 /* GL_LUMINANCE */ - 0x1902: 1,\n        8: 2,\n        29502: 3,\n        29504: 4,\n      };\n      return colorChannels[format - 0x1902]||1;\n    }\n  \n  function heapObjectForWebGLType(type) {\n      // Micro-optimization for size: Subtract lowest GL enum number (0x1400/* GL_BYTE */) from type to compare\n      // smaller values for the heap, for shorter generated code size.\n      // Also the type HEAPU16 is not tested for explicitly, but any unrecognized type will return out HEAPU16.\n      // (since most types are HEAPU16)\n      type -= 0x1400;\n  \n      if (type == 1) return HEAPU8;\n  \n      if (type == 4) return HEAP32;\n  \n      if (type == 6) return HEAPF32;\n  \n      if (type == 5\n        || type == 28922\n        )\n        return HEAPU32;\n  \n      return HEAPU16;\n    }\n  \n  function heapAccessShiftForWebGLHeap(heap) {\n      return 31 - Math.clz32(heap.BYTES_PER_ELEMENT);\n    }\n  function emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) {\n      var heap = heapObjectForWebGLType(type);\n      var shift = heapAccessShiftForWebGLHeap(heap);\n      var byteSize = 1<<shift;\n      var sizePerPixel = __colorChannelsInGlTextureFormat(format) * byteSize;\n      var bytes = computeUnpackAlignedImageSize(width, height, sizePerPixel, GL.unpackAlignment);\n      return heap.subarray(pixels >> shift, pixels + bytes >> shift);\n    }\n  function _emscripten_glReadPixels(x, y, width, height, format, type, pixels) {\n      var pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, format);\n      if (!pixelData) {\n        GL.recordError(0x500/*GL_INVALID_ENUM*/);\n        return;\n      }\n      GLctx.readPixels(x, y, width, height, format, type, pixelData);\n    }\n\n  function _emscripten_glReleaseShaderCompiler() {\n      // NOP (as allowed by GLES 2.0 spec)\n    }\n\n  function _emscripten_glRenderbufferStorage(x0, x1, x2, x3) { GLctx['renderbufferStorage'](x0, x1, x2, x3) }\n\n  function _emscripten_glSampleCoverage(value, invert) {\n      GLctx.sampleCoverage(value, !!invert);\n    }\n\n  function _emscripten_glScissor(x0, x1, x2, x3) { GLctx['scissor'](x0, x1, x2, x3) }\n\n  function _emscripten_glShaderBinary() {\n      GL.recordError(0x500/*GL_INVALID_ENUM*/);\n    }\n\n  function _emscripten_glShaderSource(shader, count, string, length) {\n      var source = GL.getSource(shader, count, string, length);\n  \n      GLctx.shaderSource(GL.shaders[shader], source);\n    }\n\n  function _emscripten_glStencilFunc(x0, x1, x2) { GLctx['stencilFunc'](x0, x1, x2) }\n\n  function _emscripten_glStencilFuncSeparate(x0, x1, x2, x3) { GLctx['stencilFuncSeparate'](x0, x1, x2, x3) }\n\n  function _emscripten_glStencilMask(x0) { GLctx['stencilMask'](x0) }\n\n  function _emscripten_glStencilMaskSeparate(x0, x1) { GLctx['stencilMaskSeparate'](x0, x1) }\n\n  function _emscripten_glStencilOp(x0, x1, x2) { GLctx['stencilOp'](x0, x1, x2) }\n\n  function _emscripten_glStencilOpSeparate(x0, x1, x2, x3) { GLctx['stencilOpSeparate'](x0, x1, x2, x3) }\n\n  function _emscripten_glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels) {\n      GLctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels ? emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, internalFormat) : null);\n    }\n\n  function _emscripten_glTexParameterf(x0, x1, x2) { GLctx['texParameterf'](x0, x1, x2) }\n\n  function _emscripten_glTexParameterfv(target, pname, params) {\n      var param = HEAPF32[((params)>>2)];\n      GLctx.texParameterf(target, pname, param);\n    }\n\n  function _emscripten_glTexParameteri(x0, x1, x2) { GLctx['texParameteri'](x0, x1, x2) }\n\n  function _emscripten_glTexParameteriv(target, pname, params) {\n      var param = HEAP32[((params)>>2)];\n      GLctx.texParameteri(target, pname, param);\n    }\n\n  function _emscripten_glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels) {\n      var pixelData = null;\n      if (pixels) pixelData = emscriptenWebGLGetTexPixelData(type, format, width, height, pixels, 0);\n      GLctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixelData);\n    }\n\n  function _emscripten_glUniform1f(location, v0) {\n      GLctx.uniform1f(GL.uniforms[location], v0);\n    }\n\n  var miniTempWebGLFloatBuffers=[];\n  function _emscripten_glUniform1fv(location, count, value) {\n  \n      if (count <= 288) {\n        // avoid allocation when uploading few enough uniforms\n        var view = miniTempWebGLFloatBuffers[count-1];\n        for (var i = 0; i < count; ++i) {\n          view[i] = HEAPF32[(((value)+(4*i))>>2)];\n        }\n      } else\n      {\n        var view = HEAPF32.subarray((value)>>2, (value+count*4)>>2);\n      }\n      GLctx.uniform1fv(GL.uniforms[location], view);\n    }\n\n  function _emscripten_glUniform1i(location, v0) {\n      GLctx.uniform1i(GL.uniforms[location], v0);\n    }\n\n  var __miniTempWebGLIntBuffers=[];\n  function _emscripten_glUniform1iv(location, count, value) {\n  \n      if (count <= 288) {\n        // avoid allocation when uploading few enough uniforms\n        var view = __miniTempWebGLIntBuffers[count-1];\n        for (var i = 0; i < count; ++i) {\n          view[i] = HEAP32[(((value)+(4*i))>>2)];\n        }\n      } else\n      {\n        var view = HEAP32.subarray((value)>>2, (value+count*4)>>2);\n      }\n      GLctx.uniform1iv(GL.uniforms[location], view);\n    }\n\n  function _emscripten_glUniform2f(location, v0, v1) {\n      GLctx.uniform2f(GL.uniforms[location], v0, v1);\n    }\n\n  function _emscripten_glUniform2fv(location, count, value) {\n  \n      if (count <= 144) {\n        // avoid allocation when uploading few enough uniforms\n        var view = miniTempWebGLFloatBuffers[2*count-1];\n        for (var i = 0; i < 2*count; i += 2) {\n          view[i] = HEAPF32[(((value)+(4*i))>>2)];\n          view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)];\n        }\n      } else\n      {\n        var view = HEAPF32.subarray((value)>>2, (value+count*8)>>2);\n      }\n      GLctx.uniform2fv(GL.uniforms[location], view);\n    }\n\n  function _emscripten_glUniform2i(location, v0, v1) {\n      GLctx.uniform2i(GL.uniforms[location], v0, v1);\n    }\n\n  function _emscripten_glUniform2iv(location, count, value) {\n  \n      if (count <= 144) {\n        // avoid allocation when uploading few enough uniforms\n        var view = __miniTempWebGLIntBuffers[2*count-1];\n        for (var i = 0; i < 2*count; i += 2) {\n          view[i] = HEAP32[(((value)+(4*i))>>2)];\n          view[i+1] = HEAP32[(((value)+(4*i+4))>>2)];\n        }\n      } else\n      {\n        var view = HEAP32.subarray((value)>>2, (value+count*8)>>2);\n      }\n      GLctx.uniform2iv(GL.uniforms[location], view);\n    }\n\n  function _emscripten_glUniform3f(location, v0, v1, v2) {\n      GLctx.uniform3f(GL.uniforms[location], v0, v1, v2);\n    }\n\n  function _emscripten_glUniform3fv(location, count, value) {\n  \n      if (count <= 96) {\n        // avoid allocation when uploading few enough uniforms\n        var view = miniTempWebGLFloatBuffers[3*count-1];\n        for (var i = 0; i < 3*count; i += 3) {\n          view[i] = HEAPF32[(((value)+(4*i))>>2)];\n          view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)];\n          view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)];\n        }\n      } else\n      {\n        var view = HEAPF32.subarray((value)>>2, (value+count*12)>>2);\n      }\n      GLctx.uniform3fv(GL.uniforms[location], view);\n    }\n\n  function _emscripten_glUniform3i(location, v0, v1, v2) {\n      GLctx.uniform3i(GL.uniforms[location], v0, v1, v2);\n    }\n\n  function _emscripten_glUniform3iv(location, count, value) {\n  \n      if (count <= 96) {\n        // avoid allocation when uploading few enough uniforms\n        var view = __miniTempWebGLIntBuffers[3*count-1];\n        for (var i = 0; i < 3*count; i += 3) {\n          view[i] = HEAP32[(((value)+(4*i))>>2)];\n          view[i+1] = HEAP32[(((value)+(4*i+4))>>2)];\n          view[i+2] = HEAP32[(((value)+(4*i+8))>>2)];\n        }\n      } else\n      {\n        var view = HEAP32.subarray((value)>>2, (value+count*12)>>2);\n      }\n      GLctx.uniform3iv(GL.uniforms[location], view);\n    }\n\n  function _emscripten_glUniform4f(location, v0, v1, v2, v3) {\n      GLctx.uniform4f(GL.uniforms[location], v0, v1, v2, v3);\n    }\n\n  function _emscripten_glUniform4fv(location, count, value) {\n  \n      if (count <= 72) {\n        // avoid allocation when uploading few enough uniforms\n        var view = miniTempWebGLFloatBuffers[4*count-1];\n        // hoist the heap out of the loop for size and for pthreads+growth.\n        var heap = HEAPF32;\n        value >>= 2;\n        for (var i = 0; i < 4 * count; i += 4) {\n          var dst = value + i;\n          view[i] = heap[dst];\n          view[i + 1] = heap[dst + 1];\n          view[i + 2] = heap[dst + 2];\n          view[i + 3] = heap[dst + 3];\n        }\n      } else\n      {\n        var view = HEAPF32.subarray((value)>>2, (value+count*16)>>2);\n      }\n      GLctx.uniform4fv(GL.uniforms[location], view);\n    }\n\n  function _emscripten_glUniform4i(location, v0, v1, v2, v3) {\n      GLctx.uniform4i(GL.uniforms[location], v0, v1, v2, v3);\n    }\n\n  function _emscripten_glUniform4iv(location, count, value) {\n  \n      if (count <= 72) {\n        // avoid allocation when uploading few enough uniforms\n        var view = __miniTempWebGLIntBuffers[4*count-1];\n        for (var i = 0; i < 4*count; i += 4) {\n          view[i] = HEAP32[(((value)+(4*i))>>2)];\n          view[i+1] = HEAP32[(((value)+(4*i+4))>>2)];\n          view[i+2] = HEAP32[(((value)+(4*i+8))>>2)];\n          view[i+3] = HEAP32[(((value)+(4*i+12))>>2)];\n        }\n      } else\n      {\n        var view = HEAP32.subarray((value)>>2, (value+count*16)>>2);\n      }\n      GLctx.uniform4iv(GL.uniforms[location], view);\n    }\n\n  function _emscripten_glUniformMatrix2fv(location, count, transpose, value) {\n  \n      if (count <= 72) {\n        // avoid allocation when uploading few enough uniforms\n        var view = miniTempWebGLFloatBuffers[4*count-1];\n        for (var i = 0; i < 4*count; i += 4) {\n          view[i] = HEAPF32[(((value)+(4*i))>>2)];\n          view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)];\n          view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)];\n          view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)];\n        }\n      } else\n      {\n        var view = HEAPF32.subarray((value)>>2, (value+count*16)>>2);\n      }\n      GLctx.uniformMatrix2fv(GL.uniforms[location], !!transpose, view);\n    }\n\n  function _emscripten_glUniformMatrix3fv(location, count, transpose, value) {\n  \n      if (count <= 32) {\n        // avoid allocation when uploading few enough uniforms\n        var view = miniTempWebGLFloatBuffers[9*count-1];\n        for (var i = 0; i < 9*count; i += 9) {\n          view[i] = HEAPF32[(((value)+(4*i))>>2)];\n          view[i+1] = HEAPF32[(((value)+(4*i+4))>>2)];\n          view[i+2] = HEAPF32[(((value)+(4*i+8))>>2)];\n          view[i+3] = HEAPF32[(((value)+(4*i+12))>>2)];\n          view[i+4] = HEAPF32[(((value)+(4*i+16))>>2)];\n          view[i+5] = HEAPF32[(((value)+(4*i+20))>>2)];\n          view[i+6] = HEAPF32[(((value)+(4*i+24))>>2)];\n          view[i+7] = HEAPF32[(((value)+(4*i+28))>>2)];\n          view[i+8] = HEAPF32[(((value)+(4*i+32))>>2)];\n        }\n      } else\n      {\n        var view = HEAPF32.subarray((value)>>2, (value+count*36)>>2);\n      }\n      GLctx.uniformMatrix3fv(GL.uniforms[location], !!transpose, view);\n    }\n\n  function _emscripten_glUniformMatrix4fv(location, count, transpose, value) {\n  \n      if (count <= 18) {\n        // avoid allocation when uploading few enough uniforms\n        var view = miniTempWebGLFloatBuffers[16*count-1];\n        // hoist the heap out of the loop for size and for pthreads+growth.\n        var heap = HEAPF32;\n        value >>= 2;\n        for (var i = 0; i < 16 * count; i += 16) {\n          var dst = value + i;\n          view[i] = heap[dst];\n          view[i + 1] = heap[dst + 1];\n          view[i + 2] = heap[dst + 2];\n          view[i + 3] = heap[dst + 3];\n          view[i + 4] = heap[dst + 4];\n          view[i + 5] = heap[dst + 5];\n          view[i + 6] = heap[dst + 6];\n          view[i + 7] = heap[dst + 7];\n          view[i + 8] = heap[dst + 8];\n          view[i + 9] = heap[dst + 9];\n          view[i + 10] = heap[dst + 10];\n          view[i + 11] = heap[dst + 11];\n          view[i + 12] = heap[dst + 12];\n          view[i + 13] = heap[dst + 13];\n          view[i + 14] = heap[dst + 14];\n          view[i + 15] = heap[dst + 15];\n        }\n      } else\n      {\n        var view = HEAPF32.subarray((value)>>2, (value+count*64)>>2);\n      }\n      GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, view);\n    }\n\n  function _emscripten_glUseProgram(program) {\n      GLctx.useProgram(GL.programs[program]);\n    }\n\n  function _emscripten_glValidateProgram(program) {\n      GLctx.validateProgram(GL.programs[program]);\n    }\n\n  function _emscripten_glVertexAttrib1f(x0, x1) { GLctx['vertexAttrib1f'](x0, x1) }\n\n  function _emscripten_glVertexAttrib1fv(index, v) {\n  \n      GLctx.vertexAttrib1f(index, HEAPF32[v>>2]);\n    }\n\n  function _emscripten_glVertexAttrib2f(x0, x1, x2) { GLctx['vertexAttrib2f'](x0, x1, x2) }\n\n  function _emscripten_glVertexAttrib2fv(index, v) {\n  \n      GLctx.vertexAttrib2f(index, HEAPF32[v>>2], HEAPF32[v+4>>2]);\n    }\n\n  function _emscripten_glVertexAttrib3f(x0, x1, x2, x3) { GLctx['vertexAttrib3f'](x0, x1, x2, x3) }\n\n  function _emscripten_glVertexAttrib3fv(index, v) {\n  \n      GLctx.vertexAttrib3f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2]);\n    }\n\n  function _emscripten_glVertexAttrib4f(x0, x1, x2, x3, x4) { GLctx['vertexAttrib4f'](x0, x1, x2, x3, x4) }\n\n  function _emscripten_glVertexAttrib4fv(index, v) {\n  \n      GLctx.vertexAttrib4f(index, HEAPF32[v>>2], HEAPF32[v+4>>2], HEAPF32[v+8>>2], HEAPF32[v+12>>2]);\n    }\n\n  function _emscripten_glVertexAttribDivisorANGLE(index, divisor) {\n      GLctx['vertexAttribDivisor'](index, divisor);\n    }\n\n  function _emscripten_glVertexAttribPointer(index, size, type, normalized, stride, ptr) {\n      GLctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr);\n    }\n\n  function _emscripten_glViewport(x0, x1, x2, x3) { GLctx['viewport'](x0, x1, x2, x3) }\n\n  function _emscripten_has_asyncify() {\n      return 0;\n    }\n\n  function _emscripten_memcpy_big(dest, src, num) {\n      HEAPU8.copyWithin(dest, src, src + num);\n    }\n\n  function doRequestFullscreen(target, strategy) {\n      if (!JSEvents.fullscreenEnabled()) return -1;\n      target = findEventTarget(target);\n      if (!target) return -4;\n  \n      if (!target.requestFullscreen\n        && !target.webkitRequestFullscreen\n        ) {\n        return -3;\n      }\n  \n      var canPerformRequests = JSEvents.canPerformEventHandlerRequests();\n  \n      // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so.\n      if (!canPerformRequests) {\n        if (strategy.deferUntilInEventHandler) {\n          JSEvents.deferCall(_JSEvents_requestFullscreen, 1 /* priority over pointer lock */, [target, strategy]);\n          return 1;\n        } else {\n          return -2;\n        }\n      }\n  \n      return _JSEvents_requestFullscreen(target, strategy);\n    }\n  function _emscripten_request_fullscreen_strategy(target, deferUntilInEventHandler, fullscreenStrategy) {\n      var strategy = {\n        scaleMode: HEAP32[((fullscreenStrategy)>>2)],\n        canvasResolutionScaleMode: HEAP32[(((fullscreenStrategy)+(4))>>2)],\n        filteringMode: HEAP32[(((fullscreenStrategy)+(8))>>2)],\n        deferUntilInEventHandler: deferUntilInEventHandler,\n        canvasResizedCallback: HEAP32[(((fullscreenStrategy)+(12))>>2)],\n        canvasResizedCallbackUserData: HEAP32[(((fullscreenStrategy)+(16))>>2)]\n      };\n  \n      return doRequestFullscreen(target, strategy);\n    }\n\n  function _emscripten_request_pointerlock(target, deferUntilInEventHandler) {\n      target = findEventTarget(target);\n      if (!target) return -4;\n      if (!target.requestPointerLock\n        && !target.msRequestPointerLock\n        ) {\n        return -1;\n      }\n  \n      var canPerformRequests = JSEvents.canPerformEventHandlerRequests();\n  \n      // Queue this function call if we're not currently in an event handler and the user saw it appropriate to do so.\n      if (!canPerformRequests) {\n        if (deferUntilInEventHandler) {\n          JSEvents.deferCall(requestPointerLock, 2 /* priority below fullscreen */, [target]);\n          return 1;\n        } else {\n          return -2;\n        }\n      }\n  \n      return requestPointerLock(target);\n    }\n\n  function _emscripten_get_heap_size() {\n      return HEAPU8.length;\n    }\n  \n  function abortOnCannotGrowMemory(requestedSize) {\n      abort('Cannot enlarge memory arrays to size ' + requestedSize + ' bytes (OOM). Either (1) compile with  -s INITIAL_MEMORY=X  with X higher than the current value ' + HEAP8.length + ', (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which allows increasing the size at runtime, or (3) if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ');\n    }\n  function _emscripten_resize_heap(requestedSize) {\n      abortOnCannotGrowMemory(requestedSize);\n    }\n\n  function _emscripten_sample_gamepad_data() {\n      return (JSEvents.lastGamepadState = (navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : null)))\n        ? 0 : -1;\n    }\n\n  function registerBeforeUnloadEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString) {\n      var beforeUnloadEventHandlerFunc = function(ev) {\n        var e = ev || event;\n  \n        // Note: This is always called on the main browser thread, since it needs synchronously return a value!\n        var confirmationMessage = wasmTable.get(callbackfunc)(eventTypeId, 0, userData);\n        \n        if (confirmationMessage) {\n          confirmationMessage = UTF8ToString(confirmationMessage);\n        }\n        if (confirmationMessage) {\n          e.preventDefault();\n          e.returnValue = confirmationMessage;\n          return confirmationMessage;\n        }\n      };\n  \n      var eventHandler = {\n        target: findEventTarget(target),\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: beforeUnloadEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_beforeunload_callback_on_thread(userData, callbackfunc, targetThread) {\n      if (typeof onbeforeunload === 'undefined') return -1;\n      // beforeunload callback can only be registered on the main browser thread, because the page will go away immediately after returning from the handler,\n      // and there is no time to start proxying it anywhere.\n      if (targetThread !== 1) return -5;\n      registerBeforeUnloadEventCallback(2, userData, true, callbackfunc, 28, \"beforeunload\");\n      return 0;\n    }\n\n  function registerFocusEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.focusEvent) JSEvents.focusEvent = _malloc( 256 );\n  \n      var focusEventHandlerFunc = function(ev) {\n        var e = ev || event;\n  \n        var nodeName = JSEvents.getNodeNameForTarget(e.target);\n        var id = e.target.id ? e.target.id : '';\n  \n        var focusEvent = JSEvents.focusEvent;\n        stringToUTF8(nodeName, focusEvent + 0, 128);\n        stringToUTF8(id, focusEvent + 128, 128);\n  \n        if (wasmTable.get(callbackfunc)(eventTypeId, focusEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: findEventTarget(target),\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: focusEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_blur_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerFocusEventCallback(target, userData, useCapture, callbackfunc, 12, \"blur\", targetThread);\n      return 0;\n    }\n\n\n  function _emscripten_set_element_css_size(target, width, height) {\n      target = findEventTarget(target);\n      if (!target) return -4;\n  \n      target.style.width = width + \"px\";\n      target.style.height = height + \"px\";\n  \n      return 0;\n    }\n\n  function _emscripten_set_focus_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerFocusEventCallback(target, userData, useCapture, callbackfunc, 13, \"focus\", targetThread);\n      return 0;\n    }\n\n  function fillFullscreenChangeEventData(eventStruct) {\n      var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement;\n      var isFullscreen = !!fullscreenElement;\n      /** @suppress{checkTypes} */\n      HEAP32[((eventStruct)>>2)] = isFullscreen;\n      HEAP32[(((eventStruct)+(4))>>2)] = JSEvents.fullscreenEnabled();\n      // If transitioning to fullscreen, report info about the element that is now fullscreen.\n      // If transitioning to windowed mode, report info about the element that just was fullscreen.\n      var reportedElement = isFullscreen ? fullscreenElement : JSEvents.previousFullscreenElement;\n      var nodeName = JSEvents.getNodeNameForTarget(reportedElement);\n      var id = (reportedElement && reportedElement.id) ? reportedElement.id : '';\n      stringToUTF8(nodeName, eventStruct + 8, 128);\n      stringToUTF8(id, eventStruct + 136, 128);\n      HEAP32[(((eventStruct)+(264))>>2)] = reportedElement ? reportedElement.clientWidth : 0;\n      HEAP32[(((eventStruct)+(268))>>2)] = reportedElement ? reportedElement.clientHeight : 0;\n      HEAP32[(((eventStruct)+(272))>>2)] = screen.width;\n      HEAP32[(((eventStruct)+(276))>>2)] = screen.height;\n      if (isFullscreen) {\n        JSEvents.previousFullscreenElement = fullscreenElement;\n      }\n    }\n  function registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.fullscreenChangeEvent) JSEvents.fullscreenChangeEvent = _malloc( 280 );\n  \n      var fullscreenChangeEventhandlerFunc = function(ev) {\n        var e = ev || event;\n  \n        var fullscreenChangeEvent = JSEvents.fullscreenChangeEvent;\n  \n        fillFullscreenChangeEventData(fullscreenChangeEvent);\n  \n        if (wasmTable.get(callbackfunc)(eventTypeId, fullscreenChangeEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: target,\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: fullscreenChangeEventhandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_fullscreenchange_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      if (!JSEvents.fullscreenEnabled()) return -1;\n      target = findEventTarget(target);\n      if (!target) return -4;\n      registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, 19, \"fullscreenchange\", targetThread);\n  \n      // Unprefixed Fullscreen API shipped in Chromium 71 (https://bugs.chromium.org/p/chromium/issues/detail?id=383813)\n      // As of Safari 13.0.3 on macOS Catalina 10.15.1 still ships with prefixed webkitfullscreenchange. TODO: revisit this check once Safari ships unprefixed version.\n      registerFullscreenChangeEventCallback(target, userData, useCapture, callbackfunc, 19, \"webkitfullscreenchange\", targetThread);\n  \n      return 0;\n    }\n\n  function registerGamepadEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.gamepadEvent) JSEvents.gamepadEvent = _malloc( 1432 );\n  \n      var gamepadEventHandlerFunc = function(ev) {\n        var e = ev || event;\n  \n        var gamepadEvent = JSEvents.gamepadEvent;\n        fillGamepadEventData(gamepadEvent, e[\"gamepad\"]);\n  \n        if (wasmTable.get(callbackfunc)(eventTypeId, gamepadEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: findEventTarget(target),\n        allowsDeferredCalls: true,\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: gamepadEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_gamepadconnected_callback_on_thread(userData, useCapture, callbackfunc, targetThread) {\n      if (!navigator.getGamepads && !navigator.webkitGetGamepads) return -1;\n      registerGamepadEventCallback(2, userData, useCapture, callbackfunc, 26, \"gamepadconnected\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_gamepaddisconnected_callback_on_thread(userData, useCapture, callbackfunc, targetThread) {\n      if (!navigator.getGamepads && !navigator.webkitGetGamepads) return -1;\n      registerGamepadEventCallback(2, userData, useCapture, callbackfunc, 27, \"gamepaddisconnected\", targetThread);\n      return 0;\n    }\n\n  function registerKeyEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.keyEvent) JSEvents.keyEvent = _malloc( 164 );\n  \n      var keyEventHandlerFunc = function(e) {\n        assert(e);\n  \n        var keyEventData = JSEvents.keyEvent;\n        var idx = keyEventData >> 2;\n  \n        HEAP32[idx + 0] = e.location;\n        HEAP32[idx + 1] = e.ctrlKey;\n        HEAP32[idx + 2] = e.shiftKey;\n        HEAP32[idx + 3] = e.altKey;\n        HEAP32[idx + 4] = e.metaKey;\n        HEAP32[idx + 5] = e.repeat;\n        HEAP32[idx + 6] = e.charCode;\n        HEAP32[idx + 7] = e.keyCode;\n        HEAP32[idx + 8] = e.which;\n        stringToUTF8(e.key || '', keyEventData + 36, 32);\n        stringToUTF8(e.code || '', keyEventData + 68, 32);\n        stringToUTF8(e.char || '', keyEventData + 100, 32);\n        stringToUTF8(e.locale || '', keyEventData + 132, 32);\n  \n        if (wasmTable.get(callbackfunc)(eventTypeId, keyEventData, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: findEventTarget(target),\n        allowsDeferredCalls: true,\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: keyEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_keydown_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerKeyEventCallback(target, userData, useCapture, callbackfunc, 2, \"keydown\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_keypress_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerKeyEventCallback(target, userData, useCapture, callbackfunc, 1, \"keypress\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_keyup_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerKeyEventCallback(target, userData, useCapture, callbackfunc, 3, \"keyup\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_main_loop_arg(func, arg, fps, simulateInfiniteLoop) {\n      var browserIterationFunc = function() { wasmTable.get(func)(arg); };\n      setMainLoop(browserIterationFunc, fps, simulateInfiniteLoop, arg);\n    }\n\n  function fillMouseEventData(eventStruct, e, target) {\n      assert(eventStruct % 4 == 0);\n      var idx = eventStruct >> 2;\n      HEAP32[idx + 0] = e.screenX;\n      HEAP32[idx + 1] = e.screenY;\n      HEAP32[idx + 2] = e.clientX;\n      HEAP32[idx + 3] = e.clientY;\n      HEAP32[idx + 4] = e.ctrlKey;\n      HEAP32[idx + 5] = e.shiftKey;\n      HEAP32[idx + 6] = e.altKey;\n      HEAP32[idx + 7] = e.metaKey;\n      HEAP16[idx*2 + 16] = e.button;\n      HEAP16[idx*2 + 17] = e.buttons;\n  \n      HEAP32[idx + 9] = e[\"movementX\"]\n        ;\n  \n      HEAP32[idx + 10] = e[\"movementY\"]\n        ;\n  \n      var rect = getBoundingClientRect(target);\n      HEAP32[idx + 11] = e.clientX - rect.left;\n      HEAP32[idx + 12] = e.clientY - rect.top;\n  \n    }\n  function registerMouseEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.mouseEvent) JSEvents.mouseEvent = _malloc( 64 );\n      target = findEventTarget(target);\n  \n      var mouseEventHandlerFunc = function(ev) {\n        var e = ev || event;\n  \n        // TODO: Make this access thread safe, or this could update live while app is reading it.\n        fillMouseEventData(JSEvents.mouseEvent, e, target);\n  \n        if (wasmTable.get(callbackfunc)(eventTypeId, JSEvents.mouseEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: target,\n        allowsDeferredCalls: eventTypeString != 'mousemove' && eventTypeString != 'mouseenter' && eventTypeString != 'mouseleave', // Mouse move events do not allow fullscreen/pointer lock requests to be handled in them!\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: mouseEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_mousedown_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerMouseEventCallback(target, userData, useCapture, callbackfunc, 5, \"mousedown\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_mouseenter_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerMouseEventCallback(target, userData, useCapture, callbackfunc, 33, \"mouseenter\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_mouseleave_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerMouseEventCallback(target, userData, useCapture, callbackfunc, 34, \"mouseleave\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_mousemove_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerMouseEventCallback(target, userData, useCapture, callbackfunc, 8, \"mousemove\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_mouseup_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerMouseEventCallback(target, userData, useCapture, callbackfunc, 6, \"mouseup\", targetThread);\n      return 0;\n    }\n\n  function fillPointerlockChangeEventData(eventStruct) {\n      var pointerLockElement = document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement || document.msPointerLockElement;\n      var isPointerlocked = !!pointerLockElement;\n      /** @suppress {checkTypes} */\n      HEAP32[((eventStruct)>>2)] = isPointerlocked;\n      var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement);\n      var id = (pointerLockElement && pointerLockElement.id) ? pointerLockElement.id : '';\n      stringToUTF8(nodeName, eventStruct + 4, 128);\n      stringToUTF8(id, eventStruct + 132, 128);\n    }\n  function registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.pointerlockChangeEvent) JSEvents.pointerlockChangeEvent = _malloc( 260 );\n  \n      var pointerlockChangeEventHandlerFunc = function(ev) {\n        var e = ev || event;\n  \n        var pointerlockChangeEvent = JSEvents.pointerlockChangeEvent;\n        fillPointerlockChangeEventData(pointerlockChangeEvent);\n  \n        if (wasmTable.get(callbackfunc)(eventTypeId, pointerlockChangeEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: target,\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: pointerlockChangeEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  /** @suppress {missingProperties} */\n  function _emscripten_set_pointerlockchange_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      // TODO: Currently not supported in pthreads or in --proxy-to-worker mode. (In pthreads mode, document object is not defined)\n      if (!document || !document.body || (!document.body.requestPointerLock && !document.body.mozRequestPointerLock && !document.body.webkitRequestPointerLock && !document.body.msRequestPointerLock)) {\n        return -1;\n      }\n  \n      target = findEventTarget(target);\n      if (!target) return -4;\n      registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, \"pointerlockchange\", targetThread);\n      registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, \"mozpointerlockchange\", targetThread);\n      registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, \"webkitpointerlockchange\", targetThread);\n      registerPointerlockChangeEventCallback(target, userData, useCapture, callbackfunc, 20, \"mspointerlockchange\", targetThread);\n      return 0;\n    }\n\n  function registerUiEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.uiEvent) JSEvents.uiEvent = _malloc( 36 );\n  \n      target = findEventTarget(target);\n  \n      var uiEventHandlerFunc = function(ev) {\n        var e = ev || event;\n        if (e.target != target) {\n          // Never take ui events such as scroll via a 'bubbled' route, but always from the direct element that\n          // was targeted. Otherwise e.g. if app logs a message in response to a page scroll, the Emscripten log\n          // message box could cause to scroll, generating a new (bubbled) scroll message, causing a new log print,\n          // causing a new scroll, etc..\n          return;\n        }\n        var b = document.body; // Take document.body to a variable, Closure compiler does not outline access to it on its own.\n        if (!b) {\n          // During a page unload 'body' can be null, with \"Cannot read property 'clientWidth' of null\" being thrown\n          return;\n        }\n        var uiEvent = JSEvents.uiEvent;\n        HEAP32[((uiEvent)>>2)] = e.detail;\n        HEAP32[(((uiEvent)+(4))>>2)] = b.clientWidth;\n        HEAP32[(((uiEvent)+(8))>>2)] = b.clientHeight;\n        HEAP32[(((uiEvent)+(12))>>2)] = innerWidth;\n        HEAP32[(((uiEvent)+(16))>>2)] = innerHeight;\n        HEAP32[(((uiEvent)+(20))>>2)] = outerWidth;\n        HEAP32[(((uiEvent)+(24))>>2)] = outerHeight;\n        HEAP32[(((uiEvent)+(28))>>2)] = pageXOffset;\n        HEAP32[(((uiEvent)+(32))>>2)] = pageYOffset;\n        if (wasmTable.get(callbackfunc)(eventTypeId, uiEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: target,\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: uiEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_resize_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerUiEventCallback(target, userData, useCapture, callbackfunc, 10, \"resize\", targetThread);\n      return 0;\n    }\n\n  function registerTouchEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.touchEvent) JSEvents.touchEvent = _malloc( 1684 );\n  \n      target = findEventTarget(target);\n  \n      var touchEventHandlerFunc = function(e) {\n        assert(e);\n        var touches = {};\n        var et = e.touches;\n        for(var i = 0; i < et.length; ++i) {\n          var touch = et[i];\n          // Verify that browser does not recycle touch objects with old stale data, but reports new ones each time.\n          assert(!touch.isChanged);\n          assert(!touch.onTarget);\n          touches[touch.identifier] = touch;\n        }\n        et = e.changedTouches;\n        for(var i = 0; i < et.length; ++i) {\n          var touch = et[i];\n          // Verify that browser does not recycle touch objects with old stale data, but reports new ones each time.\n          assert(!touch.onTarget);\n          touch.isChanged = 1;\n          touches[touch.identifier] = touch;\n        }\n        et = e.targetTouches;\n        for(var i = 0; i < et.length; ++i) {\n          touches[et[i].identifier].onTarget = 1;\n        }\n  \n        var touchEvent = JSEvents.touchEvent;\n        var idx = touchEvent>>2; // Pre-shift the ptr to index to HEAP32 to save code size\n        HEAP32[idx + 1] = e.ctrlKey;\n        HEAP32[idx + 2] = e.shiftKey;\n        HEAP32[idx + 3] = e.altKey;\n        HEAP32[idx + 4] = e.metaKey;\n        idx += 5; // Advance to the start of the touch array.\n        var targetRect = getBoundingClientRect(target);\n        var numTouches = 0;\n        for(var i in touches) {\n          var t = touches[i];\n          HEAP32[idx + 0] = t.identifier;\n          HEAP32[idx + 1] = t.screenX;\n          HEAP32[idx + 2] = t.screenY;\n          HEAP32[idx + 3] = t.clientX;\n          HEAP32[idx + 4] = t.clientY;\n          HEAP32[idx + 5] = t.pageX;\n          HEAP32[idx + 6] = t.pageY;\n          HEAP32[idx + 7] = t.isChanged;\n          HEAP32[idx + 8] = t.onTarget;\n          HEAP32[idx + 9] = t.clientX - targetRect.left;\n          HEAP32[idx + 10] = t.clientY - targetRect.top;\n  \n          idx += 13;\n  \n          if (++numTouches > 31) {\n            break;\n          }\n        }\n        HEAP32[((touchEvent)>>2)] = numTouches;\n  \n        if (wasmTable.get(callbackfunc)(eventTypeId, touchEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: target,\n        allowsDeferredCalls: eventTypeString == 'touchstart' || eventTypeString == 'touchend',\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: touchEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_touchcancel_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerTouchEventCallback(target, userData, useCapture, callbackfunc, 25, \"touchcancel\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_touchend_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerTouchEventCallback(target, userData, useCapture, callbackfunc, 23, \"touchend\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_touchmove_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerTouchEventCallback(target, userData, useCapture, callbackfunc, 24, \"touchmove\", targetThread);\n      return 0;\n    }\n\n  function _emscripten_set_touchstart_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      registerTouchEventCallback(target, userData, useCapture, callbackfunc, 22, \"touchstart\", targetThread);\n      return 0;\n    }\n\n  function fillVisibilityChangeEventData(eventStruct) {\n      var visibilityStates = [ \"hidden\", \"visible\", \"prerender\", \"unloaded\" ];\n      var visibilityState = visibilityStates.indexOf(document.visibilityState);\n  \n      // Assigning a boolean to HEAP32 with expected type coercion.\n      /** @suppress {checkTypes} */\n      HEAP32[((eventStruct)>>2)] = document.hidden;\n      HEAP32[(((eventStruct)+(4))>>2)] = visibilityState;\n    }\n  function registerVisibilityChangeEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.visibilityChangeEvent) JSEvents.visibilityChangeEvent = _malloc( 8 );\n  \n      var visibilityChangeEventHandlerFunc = function(ev) {\n        var e = ev || event;\n  \n        var visibilityChangeEvent = JSEvents.visibilityChangeEvent;\n  \n        fillVisibilityChangeEventData(visibilityChangeEvent);\n  \n        if (wasmTable.get(callbackfunc)(eventTypeId, visibilityChangeEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: target,\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: visibilityChangeEventHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_visibilitychange_callback_on_thread(userData, useCapture, callbackfunc, targetThread) {\n    if (!specialHTMLTargets[1]) {\n      return -4;\n    }\n      registerVisibilityChangeEventCallback(specialHTMLTargets[1], userData, useCapture, callbackfunc, 21, \"visibilitychange\", targetThread);\n      return 0;\n    }\n\n  function registerWheelEventCallback(target, userData, useCapture, callbackfunc, eventTypeId, eventTypeString, targetThread) {\n      if (!JSEvents.wheelEvent) JSEvents.wheelEvent = _malloc( 96 );\n  \n      // The DOM Level 3 events spec event 'wheel'\n      var wheelHandlerFunc = function(ev) {\n        var e = ev || event;\n        var wheelEvent = JSEvents.wheelEvent;\n        fillMouseEventData(wheelEvent, e, target);\n        HEAPF64[(((wheelEvent)+(64))>>3)] = e[\"deltaX\"];\n        HEAPF64[(((wheelEvent)+(72))>>3)] = e[\"deltaY\"];\n        HEAPF64[(((wheelEvent)+(80))>>3)] = e[\"deltaZ\"];\n        HEAP32[(((wheelEvent)+(88))>>2)] = e[\"deltaMode\"];\n        if (wasmTable.get(callbackfunc)(eventTypeId, wheelEvent, userData)) e.preventDefault();\n      };\n  \n      var eventHandler = {\n        target: target,\n        allowsDeferredCalls: true,\n        eventTypeString: eventTypeString,\n        callbackfunc: callbackfunc,\n        handlerFunc: wheelHandlerFunc,\n        useCapture: useCapture\n      };\n      JSEvents.registerOrRemoveHandler(eventHandler);\n    }\n  function _emscripten_set_wheel_callback_on_thread(target, userData, useCapture, callbackfunc, targetThread) {\n      target = findEventTarget(target);\n      if (typeof target.onwheel !== 'undefined') {\n        registerWheelEventCallback(target, userData, useCapture, callbackfunc, 9, \"wheel\", targetThread);\n        return 0;\n      } else {\n        return -1;\n      }\n    }\n\n  function _emscripten_sleep() {\n      throw 'Please compile your program with async support in order to use asynchronous operations like emscripten_sleep';\n    }\n\n  function _emscripten_thread_sleep(msecs) {\n      var start = _emscripten_get_now();\n      while (_emscripten_get_now() - start < msecs) {\n        // Do nothing.\n      }\n    }\n\n  var ENV={};\n  \n  function getExecutableName() {\n      return thisProgram || './this.program';\n    }\n  function getEnvStrings() {\n      if (!getEnvStrings.strings) {\n        // Default values.\n        // Browser language detection #8751\n        var lang = ((typeof navigator === 'object' && navigator.languages && navigator.languages[0]) || 'C').replace('-', '_') + '.UTF-8';\n        var env = {\n          'USER': 'web_user',\n          'LOGNAME': 'web_user',\n          'PATH': '/',\n          'PWD': '/',\n          'HOME': '/home/web_user',\n          'LANG': lang,\n          '_': getExecutableName()\n        };\n        // Apply the user-provided values, if any.\n        for (var x in ENV) {\n          env[x] = ENV[x];\n        }\n        var strings = [];\n        for (var x in env) {\n          strings.push(x + '=' + env[x]);\n        }\n        getEnvStrings.strings = strings;\n      }\n      return getEnvStrings.strings;\n    }\n  function _environ_get(__environ, environ_buf) {try {\n  \n      var bufSize = 0;\n      getEnvStrings().forEach(function(string, i) {\n        var ptr = environ_buf + bufSize;\n        HEAP32[(((__environ)+(i * 4))>>2)] = ptr;\n        writeAsciiToMemory(string, ptr);\n        bufSize += string.length + 1;\n      });\n      return 0;\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return e.errno;\n  }\n  }\n\n  function _environ_sizes_get(penviron_count, penviron_buf_size) {try {\n  \n      var strings = getEnvStrings();\n      HEAP32[((penviron_count)>>2)] = strings.length;\n      var bufSize = 0;\n      strings.forEach(function(string) {\n        bufSize += string.length + 1;\n      });\n      HEAP32[((penviron_buf_size)>>2)] = bufSize;\n      return 0;\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return e.errno;\n  }\n  }\n\n  function _exit(status) {\n      // void _exit(int status);\n      // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html\n      exit(status);\n    }\n\n  function _fd_close(fd) {try {\n  \n      var stream = SYSCALLS.getStreamFromFD(fd);\n      FS.close(stream);\n      return 0;\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return e.errno;\n  }\n  }\n\n  function _fd_read(fd, iov, iovcnt, pnum) {try {\n  \n      var stream = SYSCALLS.getStreamFromFD(fd);\n      var num = SYSCALLS.doReadv(stream, iov, iovcnt);\n      HEAP32[((pnum)>>2)] = num\n      return 0;\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return e.errno;\n  }\n  }\n\n  function _fd_seek(fd, offset_low, offset_high, whence, newOffset) {try {\n  \n      \n      var stream = SYSCALLS.getStreamFromFD(fd);\n      var HIGH_OFFSET = 0x100000000; // 2^32\n      // use an unsigned operator on low and shift high by 32-bits\n      var offset = offset_high * HIGH_OFFSET + (offset_low >>> 0);\n  \n      var DOUBLE_LIMIT = 0x20000000000000; // 2^53\n      // we also check for equality since DOUBLE_LIMIT + 1 == DOUBLE_LIMIT\n      if (offset <= -DOUBLE_LIMIT || offset >= DOUBLE_LIMIT) {\n        return -61;\n      }\n  \n      FS.llseek(stream, offset, whence);\n      (tempI64 = [stream.position>>>0,(tempDouble=stream.position,(+(Math.abs(tempDouble))) >= 1.0 ? (tempDouble > 0.0 ? ((Math.min((+(Math.floor((tempDouble)/4294967296.0))), 4294967295.0))|0)>>>0 : (~~((+(Math.ceil((tempDouble - +(((~~(tempDouble)))>>>0))/4294967296.0)))))>>>0) : 0)],HEAP32[((newOffset)>>2)] = tempI64[0],HEAP32[(((newOffset)+(4))>>2)] = tempI64[1]);\n      if (stream.getdents && offset === 0 && whence === 0) stream.getdents = null; // reset readdir state\n      return 0;\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return e.errno;\n  }\n  }\n\n  function _fd_write(fd, iov, iovcnt, pnum) {try {\n  \n      var stream = SYSCALLS.getStreamFromFD(fd);\n      var num = SYSCALLS.doWritev(stream, iov, iovcnt);\n      HEAP32[((pnum)>>2)] = num\n      return 0;\n    } catch (e) {\n    if (typeof FS === 'undefined' || !(e instanceof FS.ErrnoError)) abort(e);\n    return e.errno;\n  }\n  }\n\n  function _gettimeofday(ptr) {\n      var now = Date.now();\n      HEAP32[((ptr)>>2)] = (now/1000)|0; // seconds\n      HEAP32[(((ptr)+(4))>>2)] = ((now % 1000)*1000)|0; // microseconds\n      return 0;\n    }\n\n  function _setTempRet0($i) {\n      setTempRet0(($i) | 0);\n    }\n\n  function _sigaction(signum, act, oldact) {\n      //int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);\n      err('Calling stub instead of sigaction()');\n      return 0;\n    }\n\n  var __sigalrm_handler=0;\n  function _signal(sig, func) {\n      if (sig == 14) {\n        __sigalrm_handler = func;\n      } else {\n        err('Calling stub instead of signal()');\n      }\n      return 0;\n    }\n\n  var readAsmConstArgsArray=[];\n  function readAsmConstArgs(sigPtr, buf) {\n      // Nobody should have mutated _readAsmConstArgsArray underneath us to be something else than an array.\n      assert(Array.isArray(readAsmConstArgsArray));\n      // The input buffer is allocated on the stack, so it must be stack-aligned.\n      assert(buf % 16 == 0);\n      readAsmConstArgsArray.length = 0;\n      var ch;\n      // Most arguments are i32s, so shift the buffer pointer so it is a plain\n      // index into HEAP32.\n      buf >>= 2;\n      while (ch = HEAPU8[sigPtr++]) {\n        assert(ch === 100/*'d'*/ || ch === 102/*'f'*/ || ch === 105 /*'i'*/);\n        // A double takes two 32-bit slots, and must also be aligned - the backend\n        // will emit padding to avoid that.\n        var double = ch < 105;\n        if (double && (buf & 1)) buf++;\n        readAsmConstArgsArray.push(double ? HEAPF64[buf++ >> 1] : HEAP32[buf]);\n        ++buf;\n      }\n      return readAsmConstArgsArray;\n    }\nvar FSNode = /** @constructor */ function(parent, name, mode, rdev) {\n    if (!parent) {\n      parent = this;  // root node sets parent to itself\n    }\n    this.parent = parent;\n    this.mount = parent.mount;\n    this.mounted = null;\n    this.id = FS.nextInode++;\n    this.name = name;\n    this.mode = mode;\n    this.node_ops = {};\n    this.stream_ops = {};\n    this.rdev = rdev;\n  };\n  var readMode = 292/*292*/ | 73/*73*/;\n  var writeMode = 146/*146*/;\n  Object.defineProperties(FSNode.prototype, {\n   read: {\n    get: /** @this{FSNode} */function() {\n     return (this.mode & readMode) === readMode;\n    },\n    set: /** @this{FSNode} */function(val) {\n     val ? this.mode |= readMode : this.mode &= ~readMode;\n    }\n   },\n   write: {\n    get: /** @this{FSNode} */function() {\n     return (this.mode & writeMode) === writeMode;\n    },\n    set: /** @this{FSNode} */function(val) {\n     val ? this.mode |= writeMode : this.mode &= ~writeMode;\n    }\n   },\n   isFolder: {\n    get: /** @this{FSNode} */function() {\n     return FS.isDir(this.mode);\n    }\n   },\n   isDevice: {\n    get: /** @this{FSNode} */function() {\n     return FS.isChrdev(this.mode);\n    }\n   }\n  });\n  FS.FSNode = FSNode;\n  FS.staticInit();;\nModule[\"requestFullscreen\"] = function Module_requestFullscreen(lockPointer, resizeCanvas) { Browser.requestFullscreen(lockPointer, resizeCanvas) };\n  Module[\"requestFullScreen\"] = function Module_requestFullScreen() { Browser.requestFullScreen() };\n  Module[\"requestAnimationFrame\"] = function Module_requestAnimationFrame(func) { Browser.requestAnimationFrame(func) };\n  Module[\"setCanvasSize\"] = function Module_setCanvasSize(width, height, noUpdates) { Browser.setCanvasSize(width, height, noUpdates) };\n  Module[\"pauseMainLoop\"] = function Module_pauseMainLoop() { Browser.mainLoop.pause() };\n  Module[\"resumeMainLoop\"] = function Module_resumeMainLoop() { Browser.mainLoop.resume() };\n  Module[\"getUserMedia\"] = function Module_getUserMedia() { Browser.getUserMedia() }\n  Module[\"createContext\"] = function Module_createContext(canvas, useWebGL, setInModule, webGLContextAttributes) { return Browser.createContext(canvas, useWebGL, setInModule, webGLContextAttributes) };\nvar GLctx;;\nfor (var i = 0; i < 32; ++i) tempFixedLengthArray.push(new Array(i));;\nvar miniTempWebGLFloatBuffersStorage = new Float32Array(288);\n  for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) {\n  miniTempWebGLFloatBuffers[i] = miniTempWebGLFloatBuffersStorage.subarray(0, i+1);\n  }\n  ;\nvar __miniTempWebGLIntBuffersStorage = new Int32Array(288);\n  for (/**@suppress{duplicate}*/var i = 0; i < 288; ++i) {\n  __miniTempWebGLIntBuffers[i] = __miniTempWebGLIntBuffersStorage.subarray(0, i+1);\n  }\n  ;\nvar ASSERTIONS = true;\n\n\n\n/** @type {function(string, boolean=, number=)} */\nfunction intArrayFromString(stringy, dontAddNull, length) {\n  var len = length > 0 ? length : lengthBytesUTF8(stringy)+1;\n  var u8array = new Array(len);\n  var numBytesWritten = stringToUTF8Array(stringy, u8array, 0, u8array.length);\n  if (dontAddNull) u8array.length = numBytesWritten;\n  return u8array;\n}\n\nfunction intArrayToString(array) {\n  var ret = [];\n  for (var i = 0; i < array.length; i++) {\n    var chr = array[i];\n    if (chr > 0xFF) {\n      if (ASSERTIONS) {\n        assert(false, 'Character code ' + chr + ' (' + String.fromCharCode(chr) + ')  at offset ' + i + ' not in 0x00-0xFF.');\n      }\n      chr &= 0xFF;\n    }\n    ret.push(String.fromCharCode(chr));\n  }\n  return ret.join('');\n}\n\n\nvar asmLibraryArg = {\n  \"__assert_fail\": ___assert_fail,\n  \"__sys_fcntl64\": ___sys_fcntl64,\n  \"__sys_ioctl\": ___sys_ioctl,\n  \"__sys_open\": ___sys_open,\n  \"clock_gettime\": _clock_gettime,\n  \"dlclose\": _dlclose,\n  \"eglBindAPI\": _eglBindAPI,\n  \"eglChooseConfig\": _eglChooseConfig,\n  \"eglCreateContext\": _eglCreateContext,\n  \"eglCreateWindowSurface\": _eglCreateWindowSurface,\n  \"eglDestroyContext\": _eglDestroyContext,\n  \"eglDestroySurface\": _eglDestroySurface,\n  \"eglGetConfigAttrib\": _eglGetConfigAttrib,\n  \"eglGetDisplay\": _eglGetDisplay,\n  \"eglGetError\": _eglGetError,\n  \"eglInitialize\": _eglInitialize,\n  \"eglMakeCurrent\": _eglMakeCurrent,\n  \"eglQueryString\": _eglQueryString,\n  \"eglSwapBuffers\": _eglSwapBuffers,\n  \"eglSwapInterval\": _eglSwapInterval,\n  \"eglTerminate\": _eglTerminate,\n  \"eglWaitGL\": _eglWaitGL,\n  \"eglWaitNative\": _eglWaitNative,\n  \"emscripten_asm_const_int\": _emscripten_asm_const_int,\n  \"emscripten_async_wget\": _emscripten_async_wget,\n  \"emscripten_exit_fullscreen\": _emscripten_exit_fullscreen,\n  \"emscripten_exit_pointerlock\": _emscripten_exit_pointerlock,\n  \"emscripten_get_device_pixel_ratio\": _emscripten_get_device_pixel_ratio,\n  \"emscripten_get_element_css_size\": _emscripten_get_element_css_size,\n  \"emscripten_get_gamepad_status\": _emscripten_get_gamepad_status,\n  \"emscripten_get_num_gamepads\": _emscripten_get_num_gamepads,\n  \"emscripten_glActiveTexture\": _emscripten_glActiveTexture,\n  \"emscripten_glAttachShader\": _emscripten_glAttachShader,\n  \"emscripten_glBeginQueryEXT\": _emscripten_glBeginQueryEXT,\n  \"emscripten_glBindAttribLocation\": _emscripten_glBindAttribLocation,\n  \"emscripten_glBindBuffer\": _emscripten_glBindBuffer,\n  \"emscripten_glBindFramebuffer\": _emscripten_glBindFramebuffer,\n  \"emscripten_glBindRenderbuffer\": _emscripten_glBindRenderbuffer,\n  \"emscripten_glBindTexture\": _emscripten_glBindTexture,\n  \"emscripten_glBindVertexArrayOES\": _emscripten_glBindVertexArrayOES,\n  \"emscripten_glBlendColor\": _emscripten_glBlendColor,\n  \"emscripten_glBlendEquation\": _emscripten_glBlendEquation,\n  \"emscripten_glBlendEquationSeparate\": _emscripten_glBlendEquationSeparate,\n  \"emscripten_glBlendFunc\": _emscripten_glBlendFunc,\n  \"emscripten_glBlendFuncSeparate\": _emscripten_glBlendFuncSeparate,\n  \"emscripten_glBufferData\": _emscripten_glBufferData,\n  \"emscripten_glBufferSubData\": _emscripten_glBufferSubData,\n  \"emscripten_glCheckFramebufferStatus\": _emscripten_glCheckFramebufferStatus,\n  \"emscripten_glClear\": _emscripten_glClear,\n  \"emscripten_glClearColor\": _emscripten_glClearColor,\n  \"emscripten_glClearDepthf\": _emscripten_glClearDepthf,\n  \"emscripten_glClearStencil\": _emscripten_glClearStencil,\n  \"emscripten_glColorMask\": _emscripten_glColorMask,\n  \"emscripten_glCompileShader\": _emscripten_glCompileShader,\n  \"emscripten_glCompressedTexImage2D\": _emscripten_glCompressedTexImage2D,\n  \"emscripten_glCompressedTexSubImage2D\": _emscripten_glCompressedTexSubImage2D,\n  \"emscripten_glCopyTexImage2D\": _emscripten_glCopyTexImage2D,\n  \"emscripten_glCopyTexSubImage2D\": _emscripten_glCopyTexSubImage2D,\n  \"emscripten_glCreateProgram\": _emscripten_glCreateProgram,\n  \"emscripten_glCreateShader\": _emscripten_glCreateShader,\n  \"emscripten_glCullFace\": _emscripten_glCullFace,\n  \"emscripten_glDeleteBuffers\": _emscripten_glDeleteBuffers,\n  \"emscripten_glDeleteFramebuffers\": _emscripten_glDeleteFramebuffers,\n  \"emscripten_glDeleteProgram\": _emscripten_glDeleteProgram,\n  \"emscripten_glDeleteQueriesEXT\": _emscripten_glDeleteQueriesEXT,\n  \"emscripten_glDeleteRenderbuffers\": _emscripten_glDeleteRenderbuffers,\n  \"emscripten_glDeleteShader\": _emscripten_glDeleteShader,\n  \"emscripten_glDeleteTextures\": _emscripten_glDeleteTextures,\n  \"emscripten_glDeleteVertexArraysOES\": _emscripten_glDeleteVertexArraysOES,\n  \"emscripten_glDepthFunc\": _emscripten_glDepthFunc,\n  \"emscripten_glDepthMask\": _emscripten_glDepthMask,\n  \"emscripten_glDepthRangef\": _emscripten_glDepthRangef,\n  \"emscripten_glDetachShader\": _emscripten_glDetachShader,\n  \"emscripten_glDisable\": _emscripten_glDisable,\n  \"emscripten_glDisableVertexAttribArray\": _emscripten_glDisableVertexAttribArray,\n  \"emscripten_glDrawArrays\": _emscripten_glDrawArrays,\n  \"emscripten_glDrawArraysInstancedANGLE\": _emscripten_glDrawArraysInstancedANGLE,\n  \"emscripten_glDrawBuffersWEBGL\": _emscripten_glDrawBuffersWEBGL,\n  \"emscripten_glDrawElements\": _emscripten_glDrawElements,\n  \"emscripten_glDrawElementsInstancedANGLE\": _emscripten_glDrawElementsInstancedANGLE,\n  \"emscripten_glEnable\": _emscripten_glEnable,\n  \"emscripten_glEnableVertexAttribArray\": _emscripten_glEnableVertexAttribArray,\n  \"emscripten_glEndQueryEXT\": _emscripten_glEndQueryEXT,\n  \"emscripten_glFinish\": _emscripten_glFinish,\n  \"emscripten_glFlush\": _emscripten_glFlush,\n  \"emscripten_glFramebufferRenderbuffer\": _emscripten_glFramebufferRenderbuffer,\n  \"emscripten_glFramebufferTexture2D\": _emscripten_glFramebufferTexture2D,\n  \"emscripten_glFrontFace\": _emscripten_glFrontFace,\n  \"emscripten_glGenBuffers\": _emscripten_glGenBuffers,\n  \"emscripten_glGenFramebuffers\": _emscripten_glGenFramebuffers,\n  \"emscripten_glGenQueriesEXT\": _emscripten_glGenQueriesEXT,\n  \"emscripten_glGenRenderbuffers\": _emscripten_glGenRenderbuffers,\n  \"emscripten_glGenTextures\": _emscripten_glGenTextures,\n  \"emscripten_glGenVertexArraysOES\": _emscripten_glGenVertexArraysOES,\n  \"emscripten_glGenerateMipmap\": _emscripten_glGenerateMipmap,\n  \"emscripten_glGetActiveAttrib\": _emscripten_glGetActiveAttrib,\n  \"emscripten_glGetActiveUniform\": _emscripten_glGetActiveUniform,\n  \"emscripten_glGetAttachedShaders\": _emscripten_glGetAttachedShaders,\n  \"emscripten_glGetAttribLocation\": _emscripten_glGetAttribLocation,\n  \"emscripten_glGetBooleanv\": _emscripten_glGetBooleanv,\n  \"emscripten_glGetBufferParameteriv\": _emscripten_glGetBufferParameteriv,\n  \"emscripten_glGetError\": _emscripten_glGetError,\n  \"emscripten_glGetFloatv\": _emscripten_glGetFloatv,\n  \"emscripten_glGetFramebufferAttachmentParameteriv\": _emscripten_glGetFramebufferAttachmentParameteriv,\n  \"emscripten_glGetIntegerv\": _emscripten_glGetIntegerv,\n  \"emscripten_glGetProgramInfoLog\": _emscripten_glGetProgramInfoLog,\n  \"emscripten_glGetProgramiv\": _emscripten_glGetProgramiv,\n  \"emscripten_glGetQueryObjecti64vEXT\": _emscripten_glGetQueryObjecti64vEXT,\n  \"emscripten_glGetQueryObjectivEXT\": _emscripten_glGetQueryObjectivEXT,\n  \"emscripten_glGetQueryObjectui64vEXT\": _emscripten_glGetQueryObjectui64vEXT,\n  \"emscripten_glGetQueryObjectuivEXT\": _emscripten_glGetQueryObjectuivEXT,\n  \"emscripten_glGetQueryivEXT\": _emscripten_glGetQueryivEXT,\n  \"emscripten_glGetRenderbufferParameteriv\": _emscripten_glGetRenderbufferParameteriv,\n  \"emscripten_glGetShaderInfoLog\": _emscripten_glGetShaderInfoLog,\n  \"emscripten_glGetShaderPrecisionFormat\": _emscripten_glGetShaderPrecisionFormat,\n  \"emscripten_glGetShaderSource\": _emscripten_glGetShaderSource,\n  \"emscripten_glGetShaderiv\": _emscripten_glGetShaderiv,\n  \"emscripten_glGetString\": _emscripten_glGetString,\n  \"emscripten_glGetTexParameterfv\": _emscripten_glGetTexParameterfv,\n  \"emscripten_glGetTexParameteriv\": _emscripten_glGetTexParameteriv,\n  \"emscripten_glGetUniformLocation\": _emscripten_glGetUniformLocation,\n  \"emscripten_glGetUniformfv\": _emscripten_glGetUniformfv,\n  \"emscripten_glGetUniformiv\": _emscripten_glGetUniformiv,\n  \"emscripten_glGetVertexAttribPointerv\": _emscripten_glGetVertexAttribPointerv,\n  \"emscripten_glGetVertexAttribfv\": _emscripten_glGetVertexAttribfv,\n  \"emscripten_glGetVertexAttribiv\": _emscripten_glGetVertexAttribiv,\n  \"emscripten_glHint\": _emscripten_glHint,\n  \"emscripten_glIsBuffer\": _emscripten_glIsBuffer,\n  \"emscripten_glIsEnabled\": _emscripten_glIsEnabled,\n  \"emscripten_glIsFramebuffer\": _emscripten_glIsFramebuffer,\n  \"emscripten_glIsProgram\": _emscripten_glIsProgram,\n  \"emscripten_glIsQueryEXT\": _emscripten_glIsQueryEXT,\n  \"emscripten_glIsRenderbuffer\": _emscripten_glIsRenderbuffer,\n  \"emscripten_glIsShader\": _emscripten_glIsShader,\n  \"emscripten_glIsTexture\": _emscripten_glIsTexture,\n  \"emscripten_glIsVertexArrayOES\": _emscripten_glIsVertexArrayOES,\n  \"emscripten_glLineWidth\": _emscripten_glLineWidth,\n  \"emscripten_glLinkProgram\": _emscripten_glLinkProgram,\n  \"emscripten_glPixelStorei\": _emscripten_glPixelStorei,\n  \"emscripten_glPolygonOffset\": _emscripten_glPolygonOffset,\n  \"emscripten_glQueryCounterEXT\": _emscripten_glQueryCounterEXT,\n  \"emscripten_glReadPixels\": _emscripten_glReadPixels,\n  \"emscripten_glReleaseShaderCompiler\": _emscripten_glReleaseShaderCompiler,\n  \"emscripten_glRenderbufferStorage\": _emscripten_glRenderbufferStorage,\n  \"emscripten_glSampleCoverage\": _emscripten_glSampleCoverage,\n  \"emscripten_glScissor\": _emscripten_glScissor,\n  \"emscripten_glShaderBinary\": _emscripten_glShaderBinary,\n  \"emscripten_glShaderSource\": _emscripten_glShaderSource,\n  \"emscripten_glStencilFunc\": _emscripten_glStencilFunc,\n  \"emscripten_glStencilFuncSeparate\": _emscripten_glStencilFuncSeparate,\n  \"emscripten_glStencilMask\": _emscripten_glStencilMask,\n  \"emscripten_glStencilMaskSeparate\": _emscripten_glStencilMaskSeparate,\n  \"emscripten_glStencilOp\": _emscripten_glStencilOp,\n  \"emscripten_glStencilOpSeparate\": _emscripten_glStencilOpSeparate,\n  \"emscripten_glTexImage2D\": _emscripten_glTexImage2D,\n  \"emscripten_glTexParameterf\": _emscripten_glTexParameterf,\n  \"emscripten_glTexParameterfv\": _emscripten_glTexParameterfv,\n  \"emscripten_glTexParameteri\": _emscripten_glTexParameteri,\n  \"emscripten_glTexParameteriv\": _emscripten_glTexParameteriv,\n  \"emscripten_glTexSubImage2D\": _emscripten_glTexSubImage2D,\n  \"emscripten_glUniform1f\": _emscripten_glUniform1f,\n  \"emscripten_glUniform1fv\": _emscripten_glUniform1fv,\n  \"emscripten_glUniform1i\": _emscripten_glUniform1i,\n  \"emscripten_glUniform1iv\": _emscripten_glUniform1iv,\n  \"emscripten_glUniform2f\": _emscripten_glUniform2f,\n  \"emscripten_glUniform2fv\": _emscripten_glUniform2fv,\n  \"emscripten_glUniform2i\": _emscripten_glUniform2i,\n  \"emscripten_glUniform2iv\": _emscripten_glUniform2iv,\n  \"emscripten_glUniform3f\": _emscripten_glUniform3f,\n  \"emscripten_glUniform3fv\": _emscripten_glUniform3fv,\n  \"emscripten_glUniform3i\": _emscripten_glUniform3i,\n  \"emscripten_glUniform3iv\": _emscripten_glUniform3iv,\n  \"emscripten_glUniform4f\": _emscripten_glUniform4f,\n  \"emscripten_glUniform4fv\": _emscripten_glUniform4fv,\n  \"emscripten_glUniform4i\": _emscripten_glUniform4i,\n  \"emscripten_glUniform4iv\": _emscripten_glUniform4iv,\n  \"emscripten_glUniformMatrix2fv\": _emscripten_glUniformMatrix2fv,\n  \"emscripten_glUniformMatrix3fv\": _emscripten_glUniformMatrix3fv,\n  \"emscripten_glUniformMatrix4fv\": _emscripten_glUniformMatrix4fv,\n  \"emscripten_glUseProgram\": _emscripten_glUseProgram,\n  \"emscripten_glValidateProgram\": _emscripten_glValidateProgram,\n  \"emscripten_glVertexAttrib1f\": _emscripten_glVertexAttrib1f,\n  \"emscripten_glVertexAttrib1fv\": _emscripten_glVertexAttrib1fv,\n  \"emscripten_glVertexAttrib2f\": _emscripten_glVertexAttrib2f,\n  \"emscripten_glVertexAttrib2fv\": _emscripten_glVertexAttrib2fv,\n  \"emscripten_glVertexAttrib3f\": _emscripten_glVertexAttrib3f,\n  \"emscripten_glVertexAttrib3fv\": _emscripten_glVertexAttrib3fv,\n  \"emscripten_glVertexAttrib4f\": _emscripten_glVertexAttrib4f,\n  \"emscripten_glVertexAttrib4fv\": _emscripten_glVertexAttrib4fv,\n  \"emscripten_glVertexAttribDivisorANGLE\": _emscripten_glVertexAttribDivisorANGLE,\n  \"emscripten_glVertexAttribPointer\": _emscripten_glVertexAttribPointer,\n  \"emscripten_glViewport\": _emscripten_glViewport,\n  \"emscripten_has_asyncify\": _emscripten_has_asyncify,\n  \"emscripten_memcpy_big\": _emscripten_memcpy_big,\n  \"emscripten_request_fullscreen_strategy\": _emscripten_request_fullscreen_strategy,\n  \"emscripten_request_pointerlock\": _emscripten_request_pointerlock,\n  \"emscripten_resize_heap\": _emscripten_resize_heap,\n  \"emscripten_sample_gamepad_data\": _emscripten_sample_gamepad_data,\n  \"emscripten_set_beforeunload_callback_on_thread\": _emscripten_set_beforeunload_callback_on_thread,\n  \"emscripten_set_blur_callback_on_thread\": _emscripten_set_blur_callback_on_thread,\n  \"emscripten_set_canvas_element_size\": _emscripten_set_canvas_element_size,\n  \"emscripten_set_element_css_size\": _emscripten_set_element_css_size,\n  \"emscripten_set_focus_callback_on_thread\": _emscripten_set_focus_callback_on_thread,\n  \"emscripten_set_fullscreenchange_callback_on_thread\": _emscripten_set_fullscreenchange_callback_on_thread,\n  \"emscripten_set_gamepadconnected_callback_on_thread\": _emscripten_set_gamepadconnected_callback_on_thread,\n  \"emscripten_set_gamepaddisconnected_callback_on_thread\": _emscripten_set_gamepaddisconnected_callback_on_thread,\n  \"emscripten_set_keydown_callback_on_thread\": _emscripten_set_keydown_callback_on_thread,\n  \"emscripten_set_keypress_callback_on_thread\": _emscripten_set_keypress_callback_on_thread,\n  \"emscripten_set_keyup_callback_on_thread\": _emscripten_set_keyup_callback_on_thread,\n  \"emscripten_set_main_loop_arg\": _emscripten_set_main_loop_arg,\n  \"emscripten_set_mousedown_callback_on_thread\": _emscripten_set_mousedown_callback_on_thread,\n  \"emscripten_set_mouseenter_callback_on_thread\": _emscripten_set_mouseenter_callback_on_thread,\n  \"emscripten_set_mouseleave_callback_on_thread\": _emscripten_set_mouseleave_callback_on_thread,\n  \"emscripten_set_mousemove_callback_on_thread\": _emscripten_set_mousemove_callback_on_thread,\n  \"emscripten_set_mouseup_callback_on_thread\": _emscripten_set_mouseup_callback_on_thread,\n  \"emscripten_set_pointerlockchange_callback_on_thread\": _emscripten_set_pointerlockchange_callback_on_thread,\n  \"emscripten_set_resize_callback_on_thread\": _emscripten_set_resize_callback_on_thread,\n  \"emscripten_set_touchcancel_callback_on_thread\": _emscripten_set_touchcancel_callback_on_thread,\n  \"emscripten_set_touchend_callback_on_thread\": _emscripten_set_touchend_callback_on_thread,\n  \"emscripten_set_touchmove_callback_on_thread\": _emscripten_set_touchmove_callback_on_thread,\n  \"emscripten_set_touchstart_callback_on_thread\": _emscripten_set_touchstart_callback_on_thread,\n  \"emscripten_set_visibilitychange_callback_on_thread\": _emscripten_set_visibilitychange_callback_on_thread,\n  \"emscripten_set_wheel_callback_on_thread\": _emscripten_set_wheel_callback_on_thread,\n  \"emscripten_sleep\": _emscripten_sleep,\n  \"emscripten_thread_sleep\": _emscripten_thread_sleep,\n  \"environ_get\": _environ_get,\n  \"environ_sizes_get\": _environ_sizes_get,\n  \"exit\": _exit,\n  \"fd_close\": _fd_close,\n  \"fd_read\": _fd_read,\n  \"fd_seek\": _fd_seek,\n  \"fd_write\": _fd_write,\n  \"gettimeofday\": _gettimeofday,\n  \"setTempRet0\": _setTempRet0,\n  \"sigaction\": _sigaction,\n  \"signal\": _signal\n};\nvar asm = createWasm();\n/** @type {function(...*):?} */\nvar ___wasm_call_ctors = Module[\"___wasm_call_ctors\"] = createExportWrapper(\"__wasm_call_ctors\");\n\n/** @type {function(...*):?} */\nvar _free = Module[\"_free\"] = createExportWrapper(\"free\");\n\n/** @type {function(...*):?} */\nvar _memset = Module[\"_memset\"] = createExportWrapper(\"memset\");\n\n/** @type {function(...*):?} */\nvar _malloc = Module[\"_malloc\"] = createExportWrapper(\"malloc\");\n\n/** @type {function(...*):?} */\nvar _memcpy = Module[\"_memcpy\"] = createExportWrapper(\"memcpy\");\n\n/** @type {function(...*):?} */\nvar _main = Module[\"_main\"] = createExportWrapper(\"main\");\n\n/** @type {function(...*):?} */\nvar ___errno_location = Module[\"___errno_location\"] = createExportWrapper(\"__errno_location\");\n\n/** @type {function(...*):?} */\nvar _fflush = Module[\"_fflush\"] = createExportWrapper(\"fflush\");\n\n/** @type {function(...*):?} */\nvar stackSave = Module[\"stackSave\"] = createExportWrapper(\"stackSave\");\n\n/** @type {function(...*):?} */\nvar stackRestore = Module[\"stackRestore\"] = createExportWrapper(\"stackRestore\");\n\n/** @type {function(...*):?} */\nvar stackAlloc = Module[\"stackAlloc\"] = createExportWrapper(\"stackAlloc\");\n\n/** @type {function(...*):?} */\nvar _emscripten_stack_init = Module[\"_emscripten_stack_init\"] = function() {\n  return (_emscripten_stack_init = Module[\"_emscripten_stack_init\"] = Module[\"asm\"][\"emscripten_stack_init\"]).apply(null, arguments);\n};\n\n/** @type {function(...*):?} */\nvar _emscripten_stack_get_free = Module[\"_emscripten_stack_get_free\"] = function() {\n  return (_emscripten_stack_get_free = Module[\"_emscripten_stack_get_free\"] = Module[\"asm\"][\"emscripten_stack_get_free\"]).apply(null, arguments);\n};\n\n/** @type {function(...*):?} */\nvar _emscripten_stack_get_end = Module[\"_emscripten_stack_get_end\"] = function() {\n  return (_emscripten_stack_get_end = Module[\"_emscripten_stack_get_end\"] = Module[\"asm\"][\"emscripten_stack_get_end\"]).apply(null, arguments);\n};\n\n/** @type {function(...*):?} */\nvar dynCall_jiji = Module[\"dynCall_jiji\"] = createExportWrapper(\"dynCall_jiji\");\n\n/** @type {function(...*):?} */\nvar dynCall_ji = Module[\"dynCall_ji\"] = createExportWrapper(\"dynCall_ji\");\n\n\n\n\n\n// === Auto-generated postamble setup entry stuff ===\n\nif (!Object.getOwnPropertyDescriptor(Module, \"intArrayFromString\")) Module[\"intArrayFromString\"] = function() { abort(\"'intArrayFromString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"intArrayToString\")) Module[\"intArrayToString\"] = function() { abort(\"'intArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"ccall\")) Module[\"ccall\"] = function() { abort(\"'ccall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"cwrap\")) Module[\"cwrap\"] = function() { abort(\"'cwrap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"setValue\")) Module[\"setValue\"] = function() { abort(\"'setValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getValue\")) Module[\"getValue\"] = function() { abort(\"'getValue' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"allocate\")) Module[\"allocate\"] = function() { abort(\"'allocate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"UTF8ArrayToString\")) Module[\"UTF8ArrayToString\"] = function() { abort(\"'UTF8ArrayToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"UTF8ToString\")) Module[\"UTF8ToString\"] = function() { abort(\"'UTF8ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stringToUTF8Array\")) Module[\"stringToUTF8Array\"] = function() { abort(\"'stringToUTF8Array' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stringToUTF8\")) Module[\"stringToUTF8\"] = function() { abort(\"'stringToUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"lengthBytesUTF8\")) Module[\"lengthBytesUTF8\"] = function() { abort(\"'lengthBytesUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stackTrace\")) Module[\"stackTrace\"] = function() { abort(\"'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"addOnPreRun\")) Module[\"addOnPreRun\"] = function() { abort(\"'addOnPreRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"addOnInit\")) Module[\"addOnInit\"] = function() { abort(\"'addOnInit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"addOnPreMain\")) Module[\"addOnPreMain\"] = function() { abort(\"'addOnPreMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"addOnExit\")) Module[\"addOnExit\"] = function() { abort(\"'addOnExit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"addOnPostRun\")) Module[\"addOnPostRun\"] = function() { abort(\"'addOnPostRun' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeStringToMemory\")) Module[\"writeStringToMemory\"] = function() { abort(\"'writeStringToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeArrayToMemory\")) Module[\"writeArrayToMemory\"] = function() { abort(\"'writeArrayToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeAsciiToMemory\")) Module[\"writeAsciiToMemory\"] = function() { abort(\"'writeAsciiToMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"addRunDependency\")) Module[\"addRunDependency\"] = function() { abort(\"'addRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"removeRunDependency\")) Module[\"removeRunDependency\"] = function() { abort(\"'removeRunDependency' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS_createFolder\")) Module[\"FS_createFolder\"] = function() { abort(\"'FS_createFolder' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS_createPath\")) Module[\"FS_createPath\"] = function() { abort(\"'FS_createPath' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS_createDataFile\")) Module[\"FS_createDataFile\"] = function() { abort(\"'FS_createDataFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS_createPreloadedFile\")) Module[\"FS_createPreloadedFile\"] = function() { abort(\"'FS_createPreloadedFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS_createLazyFile\")) Module[\"FS_createLazyFile\"] = function() { abort(\"'FS_createLazyFile' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS_createLink\")) Module[\"FS_createLink\"] = function() { abort(\"'FS_createLink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS_createDevice\")) Module[\"FS_createDevice\"] = function() { abort(\"'FS_createDevice' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS_unlink\")) Module[\"FS_unlink\"] = function() { abort(\"'FS_unlink' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ). Alternatively, forcing filesystem support (-s FORCE_FILESYSTEM=1) can export this for you\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getLEB\")) Module[\"getLEB\"] = function() { abort(\"'getLEB' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getFunctionTables\")) Module[\"getFunctionTables\"] = function() { abort(\"'getFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"alignFunctionTables\")) Module[\"alignFunctionTables\"] = function() { abort(\"'alignFunctionTables' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerFunctions\")) Module[\"registerFunctions\"] = function() { abort(\"'registerFunctions' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"addFunction\")) Module[\"addFunction\"] = function() { abort(\"'addFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"removeFunction\")) Module[\"removeFunction\"] = function() { abort(\"'removeFunction' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getFuncWrapper\")) Module[\"getFuncWrapper\"] = function() { abort(\"'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"prettyPrint\")) Module[\"prettyPrint\"] = function() { abort(\"'prettyPrint' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"makeBigInt\")) Module[\"makeBigInt\"] = function() { abort(\"'makeBigInt' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"dynCall\")) Module[\"dynCall\"] = function() { abort(\"'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getCompilerSetting\")) Module[\"getCompilerSetting\"] = function() { abort(\"'getCompilerSetting' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"print\")) Module[\"print\"] = function() { abort(\"'print' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"printErr\")) Module[\"printErr\"] = function() { abort(\"'printErr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getTempRet0\")) Module[\"getTempRet0\"] = function() { abort(\"'getTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"setTempRet0\")) Module[\"setTempRet0\"] = function() { abort(\"'setTempRet0' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"callMain\")) Module[\"callMain\"] = function() { abort(\"'callMain' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"abort\")) Module[\"abort\"] = function() { abort(\"'abort' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stringToNewUTF8\")) Module[\"stringToNewUTF8\"] = function() { abort(\"'stringToNewUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"setFileTime\")) Module[\"setFileTime\"] = function() { abort(\"'setFileTime' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"abortOnCannotGrowMemory\")) Module[\"abortOnCannotGrowMemory\"] = function() { abort(\"'abortOnCannotGrowMemory' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"emscripten_realloc_buffer\")) Module[\"emscripten_realloc_buffer\"] = function() { abort(\"'emscripten_realloc_buffer' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"ENV\")) Module[\"ENV\"] = function() { abort(\"'ENV' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"ERRNO_CODES\")) Module[\"ERRNO_CODES\"] = function() { abort(\"'ERRNO_CODES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"ERRNO_MESSAGES\")) Module[\"ERRNO_MESSAGES\"] = function() { abort(\"'ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"setErrNo\")) Module[\"setErrNo\"] = function() { abort(\"'setErrNo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"inetPton4\")) Module[\"inetPton4\"] = function() { abort(\"'inetPton4' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"inetNtop4\")) Module[\"inetNtop4\"] = function() { abort(\"'inetNtop4' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"inetPton6\")) Module[\"inetPton6\"] = function() { abort(\"'inetPton6' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"inetNtop6\")) Module[\"inetNtop6\"] = function() { abort(\"'inetNtop6' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"readSockaddr\")) Module[\"readSockaddr\"] = function() { abort(\"'readSockaddr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeSockaddr\")) Module[\"writeSockaddr\"] = function() { abort(\"'writeSockaddr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"DNS\")) Module[\"DNS\"] = function() { abort(\"'DNS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getHostByName\")) Module[\"getHostByName\"] = function() { abort(\"'getHostByName' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"GAI_ERRNO_MESSAGES\")) Module[\"GAI_ERRNO_MESSAGES\"] = function() { abort(\"'GAI_ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"Protocols\")) Module[\"Protocols\"] = function() { abort(\"'Protocols' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"Sockets\")) Module[\"Sockets\"] = function() { abort(\"'Sockets' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getRandomDevice\")) Module[\"getRandomDevice\"] = function() { abort(\"'getRandomDevice' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"traverseStack\")) Module[\"traverseStack\"] = function() { abort(\"'traverseStack' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"UNWIND_CACHE\")) Module[\"UNWIND_CACHE\"] = function() { abort(\"'UNWIND_CACHE' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"withBuiltinMalloc\")) Module[\"withBuiltinMalloc\"] = function() { abort(\"'withBuiltinMalloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"readAsmConstArgsArray\")) Module[\"readAsmConstArgsArray\"] = function() { abort(\"'readAsmConstArgsArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"readAsmConstArgs\")) Module[\"readAsmConstArgs\"] = function() { abort(\"'readAsmConstArgs' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"mainThreadEM_ASM\")) Module[\"mainThreadEM_ASM\"] = function() { abort(\"'mainThreadEM_ASM' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"jstoi_q\")) Module[\"jstoi_q\"] = function() { abort(\"'jstoi_q' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"jstoi_s\")) Module[\"jstoi_s\"] = function() { abort(\"'jstoi_s' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getExecutableName\")) Module[\"getExecutableName\"] = function() { abort(\"'getExecutableName' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"listenOnce\")) Module[\"listenOnce\"] = function() { abort(\"'listenOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"autoResumeAudioContext\")) Module[\"autoResumeAudioContext\"] = function() { abort(\"'autoResumeAudioContext' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"dynCallLegacy\")) Module[\"dynCallLegacy\"] = function() { abort(\"'dynCallLegacy' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getDynCaller\")) Module[\"getDynCaller\"] = function() { abort(\"'getDynCaller' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"dynCall\")) Module[\"dynCall\"] = function() { abort(\"'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"callRuntimeCallbacks\")) Module[\"callRuntimeCallbacks\"] = function() { abort(\"'callRuntimeCallbacks' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"reallyNegative\")) Module[\"reallyNegative\"] = function() { abort(\"'reallyNegative' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"unSign\")) Module[\"unSign\"] = function() { abort(\"'unSign' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"reSign\")) Module[\"reSign\"] = function() { abort(\"'reSign' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"formatString\")) Module[\"formatString\"] = function() { abort(\"'formatString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"PATH\")) Module[\"PATH\"] = function() { abort(\"'PATH' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"PATH_FS\")) Module[\"PATH_FS\"] = function() { abort(\"'PATH_FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"SYSCALLS\")) Module[\"SYSCALLS\"] = function() { abort(\"'SYSCALLS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"syscallMmap2\")) Module[\"syscallMmap2\"] = function() { abort(\"'syscallMmap2' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"syscallMunmap\")) Module[\"syscallMunmap\"] = function() { abort(\"'syscallMunmap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getSocketFromFD\")) Module[\"getSocketFromFD\"] = function() { abort(\"'getSocketFromFD' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getSocketAddress\")) Module[\"getSocketAddress\"] = function() { abort(\"'getSocketAddress' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"JSEvents\")) Module[\"JSEvents\"] = function() { abort(\"'JSEvents' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerKeyEventCallback\")) Module[\"registerKeyEventCallback\"] = function() { abort(\"'registerKeyEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"specialHTMLTargets\")) Module[\"specialHTMLTargets\"] = function() { abort(\"'specialHTMLTargets' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"maybeCStringToJsString\")) Module[\"maybeCStringToJsString\"] = function() { abort(\"'maybeCStringToJsString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"findEventTarget\")) Module[\"findEventTarget\"] = function() { abort(\"'findEventTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"findCanvasEventTarget\")) Module[\"findCanvasEventTarget\"] = function() { abort(\"'findCanvasEventTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getBoundingClientRect\")) Module[\"getBoundingClientRect\"] = function() { abort(\"'getBoundingClientRect' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillMouseEventData\")) Module[\"fillMouseEventData\"] = function() { abort(\"'fillMouseEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerMouseEventCallback\")) Module[\"registerMouseEventCallback\"] = function() { abort(\"'registerMouseEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerWheelEventCallback\")) Module[\"registerWheelEventCallback\"] = function() { abort(\"'registerWheelEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerUiEventCallback\")) Module[\"registerUiEventCallback\"] = function() { abort(\"'registerUiEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerFocusEventCallback\")) Module[\"registerFocusEventCallback\"] = function() { abort(\"'registerFocusEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillDeviceOrientationEventData\")) Module[\"fillDeviceOrientationEventData\"] = function() { abort(\"'fillDeviceOrientationEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerDeviceOrientationEventCallback\")) Module[\"registerDeviceOrientationEventCallback\"] = function() { abort(\"'registerDeviceOrientationEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillDeviceMotionEventData\")) Module[\"fillDeviceMotionEventData\"] = function() { abort(\"'fillDeviceMotionEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerDeviceMotionEventCallback\")) Module[\"registerDeviceMotionEventCallback\"] = function() { abort(\"'registerDeviceMotionEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"screenOrientation\")) Module[\"screenOrientation\"] = function() { abort(\"'screenOrientation' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillOrientationChangeEventData\")) Module[\"fillOrientationChangeEventData\"] = function() { abort(\"'fillOrientationChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerOrientationChangeEventCallback\")) Module[\"registerOrientationChangeEventCallback\"] = function() { abort(\"'registerOrientationChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillFullscreenChangeEventData\")) Module[\"fillFullscreenChangeEventData\"] = function() { abort(\"'fillFullscreenChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerFullscreenChangeEventCallback\")) Module[\"registerFullscreenChangeEventCallback\"] = function() { abort(\"'registerFullscreenChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerRestoreOldStyle\")) Module[\"registerRestoreOldStyle\"] = function() { abort(\"'registerRestoreOldStyle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"hideEverythingExceptGivenElement\")) Module[\"hideEverythingExceptGivenElement\"] = function() { abort(\"'hideEverythingExceptGivenElement' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"restoreHiddenElements\")) Module[\"restoreHiddenElements\"] = function() { abort(\"'restoreHiddenElements' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"setLetterbox\")) Module[\"setLetterbox\"] = function() { abort(\"'setLetterbox' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"currentFullscreenStrategy\")) Module[\"currentFullscreenStrategy\"] = function() { abort(\"'currentFullscreenStrategy' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"restoreOldWindowedStyle\")) Module[\"restoreOldWindowedStyle\"] = function() { abort(\"'restoreOldWindowedStyle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"softFullscreenResizeWebGLRenderTarget\")) Module[\"softFullscreenResizeWebGLRenderTarget\"] = function() { abort(\"'softFullscreenResizeWebGLRenderTarget' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"doRequestFullscreen\")) Module[\"doRequestFullscreen\"] = function() { abort(\"'doRequestFullscreen' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillPointerlockChangeEventData\")) Module[\"fillPointerlockChangeEventData\"] = function() { abort(\"'fillPointerlockChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerPointerlockChangeEventCallback\")) Module[\"registerPointerlockChangeEventCallback\"] = function() { abort(\"'registerPointerlockChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerPointerlockErrorEventCallback\")) Module[\"registerPointerlockErrorEventCallback\"] = function() { abort(\"'registerPointerlockErrorEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"requestPointerLock\")) Module[\"requestPointerLock\"] = function() { abort(\"'requestPointerLock' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillVisibilityChangeEventData\")) Module[\"fillVisibilityChangeEventData\"] = function() { abort(\"'fillVisibilityChangeEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerVisibilityChangeEventCallback\")) Module[\"registerVisibilityChangeEventCallback\"] = function() { abort(\"'registerVisibilityChangeEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerTouchEventCallback\")) Module[\"registerTouchEventCallback\"] = function() { abort(\"'registerTouchEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillGamepadEventData\")) Module[\"fillGamepadEventData\"] = function() { abort(\"'fillGamepadEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerGamepadEventCallback\")) Module[\"registerGamepadEventCallback\"] = function() { abort(\"'registerGamepadEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerBeforeUnloadEventCallback\")) Module[\"registerBeforeUnloadEventCallback\"] = function() { abort(\"'registerBeforeUnloadEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"fillBatteryEventData\")) Module[\"fillBatteryEventData\"] = function() { abort(\"'fillBatteryEventData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"battery\")) Module[\"battery\"] = function() { abort(\"'battery' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"registerBatteryEventCallback\")) Module[\"registerBatteryEventCallback\"] = function() { abort(\"'registerBatteryEventCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"setCanvasElementSize\")) Module[\"setCanvasElementSize\"] = function() { abort(\"'setCanvasElementSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getCanvasElementSize\")) Module[\"getCanvasElementSize\"] = function() { abort(\"'getCanvasElementSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"polyfillSetImmediate\")) Module[\"polyfillSetImmediate\"] = function() { abort(\"'polyfillSetImmediate' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"demangle\")) Module[\"demangle\"] = function() { abort(\"'demangle' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"demangleAll\")) Module[\"demangleAll\"] = function() { abort(\"'demangleAll' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"jsStackTrace\")) Module[\"jsStackTrace\"] = function() { abort(\"'jsStackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stackTrace\")) Module[\"stackTrace\"] = function() { abort(\"'stackTrace' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getEnvStrings\")) Module[\"getEnvStrings\"] = function() { abort(\"'getEnvStrings' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"checkWasiClock\")) Module[\"checkWasiClock\"] = function() { abort(\"'checkWasiClock' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeI53ToI64\")) Module[\"writeI53ToI64\"] = function() { abort(\"'writeI53ToI64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeI53ToI64Clamped\")) Module[\"writeI53ToI64Clamped\"] = function() { abort(\"'writeI53ToI64Clamped' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeI53ToI64Signaling\")) Module[\"writeI53ToI64Signaling\"] = function() { abort(\"'writeI53ToI64Signaling' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeI53ToU64Clamped\")) Module[\"writeI53ToU64Clamped\"] = function() { abort(\"'writeI53ToU64Clamped' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeI53ToU64Signaling\")) Module[\"writeI53ToU64Signaling\"] = function() { abort(\"'writeI53ToU64Signaling' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"readI53FromI64\")) Module[\"readI53FromI64\"] = function() { abort(\"'readI53FromI64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"readI53FromU64\")) Module[\"readI53FromU64\"] = function() { abort(\"'readI53FromU64' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"convertI32PairToI53\")) Module[\"convertI32PairToI53\"] = function() { abort(\"'convertI32PairToI53' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"convertU32PairToI53\")) Module[\"convertU32PairToI53\"] = function() { abort(\"'convertU32PairToI53' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"uncaughtExceptionCount\")) Module[\"uncaughtExceptionCount\"] = function() { abort(\"'uncaughtExceptionCount' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"exceptionLast\")) Module[\"exceptionLast\"] = function() { abort(\"'exceptionLast' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"exceptionCaught\")) Module[\"exceptionCaught\"] = function() { abort(\"'exceptionCaught' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"ExceptionInfoAttrs\")) Module[\"ExceptionInfoAttrs\"] = function() { abort(\"'ExceptionInfoAttrs' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"ExceptionInfo\")) Module[\"ExceptionInfo\"] = function() { abort(\"'ExceptionInfo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"CatchInfo\")) Module[\"CatchInfo\"] = function() { abort(\"'CatchInfo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"exception_addRef\")) Module[\"exception_addRef\"] = function() { abort(\"'exception_addRef' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"exception_decRef\")) Module[\"exception_decRef\"] = function() { abort(\"'exception_decRef' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"Browser\")) Module[\"Browser\"] = function() { abort(\"'Browser' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"funcWrappers\")) Module[\"funcWrappers\"] = function() { abort(\"'funcWrappers' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"getFuncWrapper\")) Module[\"getFuncWrapper\"] = function() { abort(\"'getFuncWrapper' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"setMainLoop\")) Module[\"setMainLoop\"] = function() { abort(\"'setMainLoop' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"FS\")) Module[\"FS\"] = function() { abort(\"'FS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"mmapAlloc\")) Module[\"mmapAlloc\"] = function() { abort(\"'mmapAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"MEMFS\")) Module[\"MEMFS\"] = function() { abort(\"'MEMFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"TTY\")) Module[\"TTY\"] = function() { abort(\"'TTY' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"PIPEFS\")) Module[\"PIPEFS\"] = function() { abort(\"'PIPEFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"SOCKFS\")) Module[\"SOCKFS\"] = function() { abort(\"'SOCKFS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"_setNetworkCallback\")) Module[\"_setNetworkCallback\"] = function() { abort(\"'_setNetworkCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"tempFixedLengthArray\")) Module[\"tempFixedLengthArray\"] = function() { abort(\"'tempFixedLengthArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"miniTempWebGLFloatBuffers\")) Module[\"miniTempWebGLFloatBuffers\"] = function() { abort(\"'miniTempWebGLFloatBuffers' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"heapObjectForWebGLType\")) Module[\"heapObjectForWebGLType\"] = function() { abort(\"'heapObjectForWebGLType' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"heapAccessShiftForWebGLHeap\")) Module[\"heapAccessShiftForWebGLHeap\"] = function() { abort(\"'heapAccessShiftForWebGLHeap' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"GL\")) Module[\"GL\"] = function() { abort(\"'GL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"emscriptenWebGLGet\")) Module[\"emscriptenWebGLGet\"] = function() { abort(\"'emscriptenWebGLGet' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"computeUnpackAlignedImageSize\")) Module[\"computeUnpackAlignedImageSize\"] = function() { abort(\"'computeUnpackAlignedImageSize' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"emscriptenWebGLGetTexPixelData\")) Module[\"emscriptenWebGLGetTexPixelData\"] = function() { abort(\"'emscriptenWebGLGetTexPixelData' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"emscriptenWebGLGetUniform\")) Module[\"emscriptenWebGLGetUniform\"] = function() { abort(\"'emscriptenWebGLGetUniform' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"emscriptenWebGLGetVertexAttrib\")) Module[\"emscriptenWebGLGetVertexAttrib\"] = function() { abort(\"'emscriptenWebGLGetVertexAttrib' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"writeGLArray\")) Module[\"writeGLArray\"] = function() { abort(\"'writeGLArray' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"AL\")) Module[\"AL\"] = function() { abort(\"'AL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"SDL_unicode\")) Module[\"SDL_unicode\"] = function() { abort(\"'SDL_unicode' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"SDL_ttfContext\")) Module[\"SDL_ttfContext\"] = function() { abort(\"'SDL_ttfContext' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"SDL_audio\")) Module[\"SDL_audio\"] = function() { abort(\"'SDL_audio' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"SDL\")) Module[\"SDL\"] = function() { abort(\"'SDL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"SDL_gfx\")) Module[\"SDL_gfx\"] = function() { abort(\"'SDL_gfx' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"GLUT\")) Module[\"GLUT\"] = function() { abort(\"'GLUT' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"EGL\")) Module[\"EGL\"] = function() { abort(\"'EGL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"GLFW_Window\")) Module[\"GLFW_Window\"] = function() { abort(\"'GLFW_Window' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"GLFW\")) Module[\"GLFW\"] = function() { abort(\"'GLFW' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"GLEW\")) Module[\"GLEW\"] = function() { abort(\"'GLEW' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"IDBStore\")) Module[\"IDBStore\"] = function() { abort(\"'IDBStore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"runAndAbortIfError\")) Module[\"runAndAbortIfError\"] = function() { abort(\"'runAndAbortIfError' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"warnOnce\")) Module[\"warnOnce\"] = function() { abort(\"'warnOnce' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stackSave\")) Module[\"stackSave\"] = function() { abort(\"'stackSave' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stackRestore\")) Module[\"stackRestore\"] = function() { abort(\"'stackRestore' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stackAlloc\")) Module[\"stackAlloc\"] = function() { abort(\"'stackAlloc' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"AsciiToString\")) Module[\"AsciiToString\"] = function() { abort(\"'AsciiToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stringToAscii\")) Module[\"stringToAscii\"] = function() { abort(\"'stringToAscii' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"UTF16ToString\")) Module[\"UTF16ToString\"] = function() { abort(\"'UTF16ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stringToUTF16\")) Module[\"stringToUTF16\"] = function() { abort(\"'stringToUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"lengthBytesUTF16\")) Module[\"lengthBytesUTF16\"] = function() { abort(\"'lengthBytesUTF16' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"UTF32ToString\")) Module[\"UTF32ToString\"] = function() { abort(\"'UTF32ToString' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"stringToUTF32\")) Module[\"stringToUTF32\"] = function() { abort(\"'stringToUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"lengthBytesUTF32\")) Module[\"lengthBytesUTF32\"] = function() { abort(\"'lengthBytesUTF32' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"allocateUTF8\")) Module[\"allocateUTF8\"] = function() { abort(\"'allocateUTF8' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nif (!Object.getOwnPropertyDescriptor(Module, \"allocateUTF8OnStack\")) Module[\"allocateUTF8OnStack\"] = function() { abort(\"'allocateUTF8OnStack' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") };\nModule[\"writeStackCookie\"] = writeStackCookie;\nModule[\"checkStackCookie\"] = checkStackCookie;\nif (!Object.getOwnPropertyDescriptor(Module, \"ALLOC_NORMAL\")) Object.defineProperty(Module, \"ALLOC_NORMAL\", { configurable: true, get: function() { abort(\"'ALLOC_NORMAL' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") } });\nif (!Object.getOwnPropertyDescriptor(Module, \"ALLOC_STACK\")) Object.defineProperty(Module, \"ALLOC_STACK\", { configurable: true, get: function() { abort(\"'ALLOC_STACK' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)\") } });\n\nvar calledRun;\n\n/**\n * @constructor\n * @this {ExitStatus}\n */\nfunction ExitStatus(status) {\n  this.name = \"ExitStatus\";\n  this.message = \"Program terminated with exit(\" + status + \")\";\n  this.status = status;\n}\n\nvar calledMain = false;\n\ndependenciesFulfilled = function runCaller() {\n  // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false)\n  if (!calledRun) run();\n  if (!calledRun) dependenciesFulfilled = runCaller; // try this again later, after new deps are fulfilled\n};\n\nfunction callMain(args) {\n  assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on Module[\"onRuntimeInitialized\"])');\n  assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called');\n\n  var entryFunction = Module['_main'];\n\n  args = args || [];\n\n  var argc = args.length+1;\n  var argv = stackAlloc((argc + 1) * 4);\n  HEAP32[argv >> 2] = allocateUTF8OnStack(thisProgram);\n  for (var i = 1; i < argc; i++) {\n    HEAP32[(argv >> 2) + i] = allocateUTF8OnStack(args[i - 1]);\n  }\n  HEAP32[(argv >> 2) + argc] = 0;\n\n  try {\n\n    var ret = entryFunction(argc, argv);\n\n    // In PROXY_TO_PTHREAD builds, we should never exit the runtime below, as\n    // execution is asynchronously handed off to a pthread.\n      // if we're not running an evented main loop, it's time to exit\n      exit(ret, /* implicit = */ true);\n  }\n  catch(e) {\n    if (e instanceof ExitStatus) {\n      // exit() throws this once it's done to make sure execution\n      // has been stopped completely\n      return;\n    } else if (e == 'unwind') {\n      // running an evented main loop, don't immediately exit\n      noExitRuntime = true;\n      return;\n    } else {\n      var toLog = e;\n      if (e && typeof e === 'object' && e.stack) {\n        toLog = [e, e.stack];\n      }\n      err('exception thrown: ' + toLog);\n      quit_(1, e);\n    }\n  } finally {\n    calledMain = true;\n\n  }\n}\n\nfunction stackCheckInit() {\n  // This is normally called automatically during __wasm_call_ctors but need to\n  // get these values before even running any of the ctors so we call it redundantly\n  // here.\n  // TODO(sbc): Move writeStackCookie to native to to avoid this.\n  _emscripten_stack_init();\n  writeStackCookie();\n}\n\n/** @type {function(Array=)} */\nfunction run(args) {\n  args = args || arguments_;\n\n  if (runDependencies > 0) {\n    return;\n  }\n\n  stackCheckInit();\n\n  preRun();\n\n  // a preRun added a dependency, run will be called later\n  if (runDependencies > 0) {\n    return;\n  }\n\n  function doRun() {\n    // run may have just been called through dependencies being fulfilled just in this very frame,\n    // or while the async setStatus time below was happening\n    if (calledRun) return;\n    calledRun = true;\n    Module['calledRun'] = true;\n\n    if (ABORT) return;\n\n    initRuntime();\n\n    preMain();\n\n    if (Module['onRuntimeInitialized']) Module['onRuntimeInitialized']();\n\n    if (shouldRunNow) callMain(args);\n\n    postRun();\n  }\n\n  if (Module['setStatus']) {\n    Module['setStatus']('Running...');\n    setTimeout(function() {\n      setTimeout(function() {\n        Module['setStatus']('');\n      }, 1);\n      doRun();\n    }, 1);\n  } else\n  {\n    doRun();\n  }\n  checkStackCookie();\n}\nModule['run'] = run;\n\nfunction checkUnflushedContent() {\n  // Compiler settings do not allow exiting the runtime, so flushing\n  // the streams is not possible. but in ASSERTIONS mode we check\n  // if there was something to flush, and if so tell the user they\n  // should request that the runtime be exitable.\n  // Normally we would not even include flush() at all, but in ASSERTIONS\n  // builds we do so just for this check, and here we see if there is any\n  // content to flush, that is, we check if there would have been\n  // something a non-ASSERTIONS build would have not seen.\n  // How we flush the streams depends on whether we are in SYSCALLS_REQUIRE_FILESYSTEM=0\n  // mode (which has its own special function for this; otherwise, all\n  // the code is inside libc)\n  var oldOut = out;\n  var oldErr = err;\n  var has = false;\n  out = err = function(x) {\n    has = true;\n  }\n  try { // it doesn't matter if it fails\n    var flush = Module['_fflush'];\n    if (flush) flush(0);\n    // also flush in the JS FS layer\n    ['stdout', 'stderr'].forEach(function(name) {\n      var info = FS.analyzePath('/dev/' + name);\n      if (!info) return;\n      var stream = info.object;\n      var rdev = stream.rdev;\n      var tty = TTY.ttys[rdev];\n      if (tty && tty.output && tty.output.length) {\n        has = true;\n      }\n    });\n  } catch(e) {}\n  out = oldOut;\n  err = oldErr;\n  if (has) {\n    warnOnce('stdio streams had content in them that was not flushed. you should set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline when you printf etc.');\n  }\n}\n\n/** @param {boolean|number=} implicit */\nfunction exit(status, implicit) {\n  checkUnflushedContent();\n\n  // if this is just main exit-ing implicitly, and the status is 0, then we\n  // don't need to do anything here and can just leave. if the status is\n  // non-zero, though, then we need to report it.\n  // (we may have warned about this earlier, if a situation justifies doing so)\n  if (implicit && noExitRuntime && status === 0) {\n    return;\n  }\n\n  if (noExitRuntime) {\n    // if exit() was called, we may warn the user if the runtime isn't actually being shut down\n    if (!implicit) {\n      var msg = 'program exited (with status: ' + status + '), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)';\n      err(msg);\n    }\n  } else {\n\n    EXITSTATUS = status;\n\n    exitRuntime();\n\n    if (Module['onExit']) Module['onExit'](status);\n\n    ABORT = true;\n  }\n\n  quit_(status, new ExitStatus(status));\n}\n\nif (Module['preInit']) {\n  if (typeof Module['preInit'] == 'function') Module['preInit'] = [Module['preInit']];\n  while (Module['preInit'].length > 0) {\n    Module['preInit'].pop()();\n  }\n}\n\n// shouldRunNow refers to calling main(), not run().\nvar shouldRunNow = true;\n\nif (Module['noInitialRun']) shouldRunNow = false;\n\nrun();\n\n\n\n\n\n"
  },
  {
    "path": "platform/web/embed/package/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n    <title>LowRes NX</title>\n    <style>\n    body {\n      background-color: #000;\n      margin: 0;\n      padding: 0;\n    }\n    .emscripten_border {\n    }\n    .emscripten {\n      display: block;\n      margin: auto;\n    }\n    </style>\n  </head>\n  <body>\n    <div class=\"emscripten_border\">\n      <canvas class=\"emscripten\" id=\"canvas\" oncontextmenu=\"event.preventDefault()\" onmousedown=\"window.focus()\"></canvas>\n    </div>\n\n    <script type='text/javascript'>\n      var Module = {\n        canvas: document.getElementById('canvas'),\n        arguments: [\"program.nx\"],\n        print: function(t) {\n          console.log(t);\n        },\n        printErr: function(t) {\n          console.log(t);\n        }\n      };\n      function onKeyDown_blocker(event) {\n        event = event || window.event;\n        if (event.keyCode == 37 || event.keyCode == 38 || event.keyCode == 39 || event.keyCode == 40) {\n          if (event.preventDefault) event.preventDefault();\n        }\n      }\n      document.addEventListener('keydown', onKeyDown_blocker, false);\n    </script>\n    <script async type=\"text/javascript\" src=\"LowResNX120.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "platform/web/embed/package/program.nx",
    "content": "PRINT \"REPLACE THIS WITH\"\nPRINT \"YOUR PROGRAM\"\n"
  },
  {
    "path": "platform/web/embed/readme.txt",
    "content": "Embeddable Web Player for LowRes NX\n\nReplace the file \"program.nx\" in the \"package\" folder with your own program. It must be called \"program.nx\". You should rename the folder to your program's name.\n\nHow to publish it on itch.io\n\nMake a zip archive of your folder and upload it to itch.io. Select \"This file will be played in the browser\" and set the viewport dimensions to 640x512. Please add the tag \"lowres-nx\". You may choose \"Automatically start on page load\", or if not, upload a screenshot for \"Embed BG\" in the theme editor."
  },
  {
    "path": "platform/web/makefile",
    "content": "# Declaration of variables\nCC = emcc\nCC_FLAGS = -w -s USE_SDL=2 -I ../../core -I ../../core/machine -I ../../core/accessories -I ../../core/datamanager -I ../../core/interpreter -I ../../core/libraries -I ../../core/overlay\nCC_FLAGS_MAIN = -s USE_SDL=2\n\n# File names\nEXEC = output/LowResNX120.js\nSOURCES = $(wildcard ../../core/*.c) $(wildcard ../../core/*/*.c) $(wildcard ../../sdl/*.c)\nOBJECTS = $(SOURCES:.c=.bc)\n \n# Main target\n$(EXEC): $(OBJECTS)\n\t@mkdir -p $(@D)\n\t$(CC) $(CC_FLAGS_MAIN) $(OBJECTS) -o $(EXEC)\n\n# To obtain object files\n%.bc: %.c\n\t$(CC) -c $(CC_FLAGS) $< -o $@\n\n# To remove generated files\nclean:\n\trm -f $(OBJECTS)"
  },
  {
    "path": "programs/Gfx Designer 2.0.nx",
    "content": "'TITLE:  GFX DESIGNER\n'AUTHOR: TIMO KLOSS, MRLEGOBOY\n\n'======================\n'** RAM MAP **\nGLOBAL USERCHARS,USERBG,USERPALS,TEMPMEM\nUSERCHARS = $A000\nUSERBG    = $B000\nUSERPALS  = $D004\nTEMPMEM   = $D024\n'======================\n\n'ENERGY SAVING MODE\nSYSTEM 0,1\n\nKEYBOARD OPTIONAL\n\nDIM GLOBAL MENU$(6)\n\nDIM GLOBAL PXL(63,63)\n\nGLOBAL SELCHAR,SELSIZE,SELPAGE,SELCOLOR\nGLOBAL SELPAL,SELPALPAGE\nGLOBAL HIDECOUNT,CHAROFFSET\n\nSELCHAR=1\nSELSIZE=0\nSELPAGE=0\nSELCOLOR=1\nSELPAL=0\nSELPALPAGE=0\n\nGLOBAL MAPW,MAPH,CELLSIZE\n\nMAPW=32\nMAPH=32\nCELLSIZE=0\n\nGLOBAL EDMODE,TOOLMODE,FLIPMODE,PRI,MAPX,MAPY,DRAGX,DRAGY\nGLOBAL STAMPW,STAMPH\nGLOBAL MAPBARST\nGLOBAL WINW,WINH\n\nEDMODE=1\n\n\nGLOBAL FTYPE$,FILESOFFS,SELFILE\nGLOBAL PALFILE,CHARFILE,BGFILE\n\nSELFILE=-1\nPALFILE=1\nCHARFILE=2\nBGFILE=3\n\nDIM GLOBAL SIZESW(5),SIZESH(5)\nDATA 10,20,32,64,96,128\nDATA 8,16,32,64,96,128\nFOR I=0 TO 5\n  READ SIZESW(I)\nNEXT I\nFOR I=0 TO 5\n  READ SIZESH(I)\nNEXT I\n\nFONT 128\n\n'DEFAULT USER PALETTE\nCOPY ROM(10),SIZE(10) TO USERPALS\n\nOK=0\nR=0\n\nFILES\nCALL BLOADCHR(OK)\nCALL BLOADPAL(OK)\nCALL BLOADBG(OK)\n\n'==== TOUCH ZONES INIT ====\n\n'SETTINGS\nGLOBAL MAX_ZONE, ZONE_PAL\n'STATUS GETTERS\nGLOBAL CUR_ZONE, ZONE_EVENT, ZONE_IN_X, ZONE_IN_Y, ZONE_RESULT\n'INTERNAL\nGLOBAL ZONE_LAST_X, ZONE_LAST_Y\n\nGLOBAL E_DOWN, E_UP, E_OUT, E_DRAG\nE_DOWN=1\nE_UP=2\nE_OUT=3\nE_DRAG=4\n\nMAX_ZONE=33\nDIM GLOBAL ZONEX(MAX_ZONE), ZONEY(MAX_ZONE), ZONEW(MAX_ZONE), ZONEH(MAX_ZONE), ZONEP(MAX_ZONE)\n\nTOUCHSCREEN\n\n'======================\n\nCHAREDITOR:\n\nHIDECOUNT=0\nCALL INITMEM\nCALL READPAL\nCALL READCHAR\nCALL READPAGE\nCALL DRAWCHARUI\n\nCALL RESETZONES\n\n' BAR BUTTONS\nZONE_PAL=5\nCALL SETZONE(0,0,14,2,2)\n' BAR TABS\nZONE_PAL=-1\nCALL SETZONE(7,14,14,2,2)\nCALL SETZONE(8,16,14,2,2)\nCALL SETZONE(9,18,14,2,2)\n\n' PAGE AREA\nZONE_PAL=-1\nCALL SETZONE(10,2,10,16,4)\n' PAGE UP/DOWN\nZONE_PAL=5\nCALL SETZONE(11,18,10,2,2)\nCALL SETZONE(12,18,12,2,2)\n' SELECTION SIZE\nCALL SETZONE(13,0,10,2,2)\nCALL SETZONE(14,0,12,2,2)\n\n' DRAW AREA\nZONE_PAL=-1\nCALL SETZONE(20,1,1,8,8)\n' COLORS AREA\nCALL SETZONE(21,10,1,2,8)\n' EDIT BUTTONS\nZONE_PAL=5\nCALL SETZONE(22,17,1,2,2)\nCALL SETZONE(23,17,3,2,2)\nCALL SETZONE(24,17,5,2,2)\nCALL SETZONE(25,17,7,2,2)\nCALL SETZONE(26,15,1,2,2)\nCALL SETZONE(27,15,3,2,2)\nCALL SETZONE(28,15,5,2,2)\nCALL SETZONE(29,15,7,2,2)\nCALL SETZONE(30,13,1,2,2)\nCALL SETZONE(31,13,3,2,2)\nCALL SETZONE(32,13,5,2,2)\nCALL SETZONE(33,13,7,2,2)\n\nDO\n  CALL UPDATEZONES\n  \n  K$=INKEY$\n  IF K$<>\"\" THEN\n    IF K$=\"2\" THEN GOTO PALEDITOR\n    IF K$=\"3\" THEN GOTO MAPEDITOR\n  END IF\n\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=8 THEN GOTO PALEDITOR\n    IF CUR_ZONE=9 THEN GOTO MAPEDITOR\n  END IF\n\n  CALL HANDLECHARSEL(0)\n\n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n\n    IF CUR_ZONE=20 THEN\n      PXL(ZONE_IN_X,ZONE_IN_Y)=SELCOLOR\n      PAL SELPAL MOD 4\n      CELL ZONE_LAST_X,ZONE_LAST_Y,16+SELCOLOR\n      CALL WRITECHAR\n    END IF\n\n    IF CUR_ZONE=21 THEN\n      SELCOLOR=ZONE_IN_Y\\2\n      CALL DRAWSELCOLOR(10,1)\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=0 THEN\n      FTYPE$=\"CHARACTERS\"\n      GOTO FILESMENU\n    END IF\n    IF CUR_ZONE=22 THEN CALL BCLEAR\n    IF CUR_ZONE=23 THEN CALL BCUT\n    IF CUR_ZONE=24 THEN CALL BCOPY\n    IF CUR_ZONE=25 THEN CALL BPASTE\n    IF CUR_ZONE=26 THEN GOTO BIGEDITOR\n    IF CUR_ZONE=27 THEN CALL BFLIPX\n    IF CUR_ZONE=28 THEN CALL BFLIPY\n    IF CUR_ZONE=29 THEN CALL BSPIN\n    IF CUR_ZONE=30 THEN CALL BMOVEU\n    IF CUR_ZONE=31 THEN CALL BMOVED\n    IF CUR_ZONE=32 THEN CALL BMOVEL\n    IF CUR_ZONE=33 THEN CALL BMOVER\n  END IF\n\n  IF HIDECOUNT>0 THEN\n    DEC HIDECOUNT\n    IF HIDECOUNT=0 THEN CALL HIDESELCHAR\n  END IF\n\n  WAIT VBL\nLOOP\n\nSUB DRAWCHARUI\n  BG COPY 0,0,20,16 TO 0,0\n  CALL INITSPRITES\n  CALL DRAWCHAR\n  CALL DRAWSELCOLOR(10,1)\n  CALL DRAWCHARNUM(0)\n  CALL DRAWPAGE\n  P=(SELPAL MOD 4)\n  BG TINT 10,1 TO 11,8 PAL P\nEND SUB\n\nSUB INITMEM\n  ON RASTER OFF\n  SPRITE OFF\n  COPY ROM(1),SIZE(1) TO $FF00\n  COPY ROM(2),SIZE(2) TO $8000\n  BG SOURCE ROM(3)\nEND SUB\n\nSUB INITSPRITES\n  SPRITE 0 PAL 6 FLIP 0,0\n  SPRITE 1 PAL 6 FLIP 1,0\n  SPRITE 2 PAL 6 FLIP 0,1\n  SPRITE 3 PAL 6 FLIP 1,1\n  SPRITE 4 PAL 6 FLIP 0,0\n  SPRITE 5 PAL 6 FLIP 1,0\n  SPRITE 6 PAL 6 FLIP 0,1\n  SPRITE 7 PAL 6 FLIP 1,1\n  SPRITE 8 PAL 6 FLIP 0,0\n  SPRITE 9 PAL 6 FLIP 1,0\n  SPRITE 10 PAL 6 FLIP 0,1\n  SPRITE 11 PAL 6 FLIP 1,1\nEND SUB\n\nSUB DRAWCHAR\n  PAL SELPAL MOD 4\n  FOR PY=0 TO 7\n    FOR PX=0 TO 7\n      CELL PX+1,PY+1,16+PXL(PX,PY)\n    NEXT PX\n  NEXT PY\nEND SUB\n\nSUB DRAWSELCOLOR(X,Y)\n  SX=X*8\n  SY=Y*8+SELCOLOR*2*8\n  SPRITE 4,SX,SY,20\n  SPRITE 5,SX+8,SY,20\n  SPRITE 6,SX,SY+8,20\n  SPRITE 7,SX+8,SY+8,20\nEND SUB\n\nSUB HANDLECHARSEL(MAP)\n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n    IF CUR_ZONE=10 THEN\n      SELCHAR=(ZONE_IN_Y*16)+ZONE_IN_X+SELPAGE*64\n      CALL FIXSEL(MAP)\n      IF NOT MAP THEN\n        CALL READCHAR\n        CALL DRAWCHAR\n      END IF\n      CALL DRAWCHARNUM(MAP)\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=11 AND SELPAGE>0 THEN\n      SELPAGE=SELPAGE-1\n      CALL READPAGE\n      CALL DRAWCHARNUM(MAP)\n    END IF\n    IF CUR_ZONE=12 AND SELPAGE<3 THEN\n      SELPAGE=SELPAGE+1\n      CALL READPAGE\n      CALL DRAWCHARNUM(MAP)\n    END IF\n    IF CUR_ZONE=13 THEN\n      IF SELSIZE>0 THEN DEC SELSIZE\n      CALL DRAWCHARNUM(MAP)\n    END IF\n    IF CUR_ZONE=14 THEN\n      IF SELSIZE<3 THEN INC SELSIZE\n      CALL FIXSEL(MAP)\n      IF NOT MAP THEN\n        CALL READCHAR\n        CALL DRAWCHAR\n      END IF\n      CALL DRAWCHARNUM(MAP)\n    END IF\n  END IF\nEND SUB\n\nSUB FIXSEL(MAP)\n  REM IF SELSIZE=0 THEN EXIT SUB\n  IF MAP AND CELLSIZE=1 THEN\n    SELCHAR=SELCHAR AND %11101110\n    IF SELSIZE>1 THEN SELSIZE=1\n    S=(SELSIZE+1)*2-1\n  ELSE\n    S=SELSIZE\n  END IF\n  N=SELCHAR MOD 64\n  X=(N MOD 16)\n  Y=(N\\16)\n  IF X+S>=16 THEN X=15-S\n  IF Y+S>=4 THEN Y=3-S\n  SELCHAR=(SELCHAR\\64*64)+Y*16+X\nEND SUB\n\nSUB DRAWCHARNUM(MAP)\n  PAL 4\n  BG FILL 2,15 TO 13,15 CHAR 128\n  TEXT 3,15,\"#\"\n  NUMBER 4,15,SELCHAR,3\n  NUMBER 8,15,SELPAGE+1,1\n  TEXT 9,15,\"/4\"\n  IF SELCHAR>=SELPAGE*64 AND SELCHAR<SELPAGE*64+64 THEN\n    IF MAP AND CELLSIZE=1 THEN\n      S=16\n      OFFS=9\n    ELSE\n      S=8\n      OFFS=1\n    END IF\n    N=SELCHAR MOD 64\n    SX=16+(N MOD 16)*8\n    SY=80+(N\\16)*8\n    SPRITE 0,SX-1,SY-1,20\n    SPRITE 1,SX+SELSIZE*S+OFFS,SY-1,20\n    SPRITE 2,SX-1,SY+SELSIZE*S+OFFS,20\n    SPRITE 3,SX+SELSIZE*S+OFFS,SY+SELSIZE*S+OFFS,20\n    HIDECOUNT=60\n  ELSE\n    CALL HIDESELCHAR\n  END IF\nEND SUB\n\nSUB HIDESELCHAR\n  SPRITE OFF 0 TO 3\nEND SUB\n\nSUB DRAWPAGE\n  PAL SELPAL MOD 4\n  CI=192\n  FOR CY=10 TO 13\n    FOR CX=2 TO 17\n      CELL CX,CY,CI\n      INC CI\n    NEXT CX\n  NEXT CY\nEND SUB\n\nSUB READPAGE\n  COPY USERCHARS+SELPAGE*$400,$400 TO $8C00\nEND SUB\n\nSUB WRITECHAR\n  CALL WRITECHARNUM(0,0)\n  CALL CHAR2PAGE\nEND SUB\n\nSUB WRITECHARSEL\n  FOR CY=0 TO SELSIZE\n    FOR CX=0 TO SELSIZE\n      CALL WRITECHARNUM(CX,CY)\n    NEXT CX\n  NEXT CY\n  CALL CHAR2PAGE\nEND SUB\n\nSUB WRITECHARNUM(CX,CY)\n  AD=USERCHARS+(SELCHAR+CX+CY*16)*16\n  FOR BI=1 TO 2\n    FOR PY=0 TO 7\n      V=0\n      FOR PX=0 TO 7\n        BV=2^(7-PX)\n        IF PXL(PX+CX*8,PY+CY*8) AND BI THEN V=V OR BV\n      NEXT PX\n      POKE AD,V\n      AD=AD+1\n    NEXT PY\n  NEXT BI\nEND SUB\n\nSUB CHAR2PAGE\n  FOR CY=0 TO SELSIZE\n    FOR CX=0 TO SELSIZE\n      CALL CHAR2PAGENUM(SELCHAR+CY*16+CX)\n    NEXT CX\n  NEXT CY\nEND SUB\n\nSUB CHAR2PAGENUM(C)\n  IF C>=SELPAGE*64 AND C<(SELPAGE+1)*64 THEN\n    COPY USERCHARS+C*16,16 TO $8C00+(C MOD 64)*16\n  END IF\nEND SUB\n\nSUB READCHAR\n  FOR CY=0 TO SELSIZE\n    FOR CX=0 TO SELSIZE\n      CALL READCHARNUM(CX,CY)\n    NEXT CX\n  NEXT CY\nEND SUB\n\nSUB READCHARNUM(CX,CY)\n  AD=USERCHARS+(SELCHAR+CX+CY*16)*16\n  FOR PY=0 TO 7\n    FOR PX=0 TO 7\n      BV=2^(7-PX)\n      PV=0\n      IF PEEK(AD) AND BV THEN PV=1\n      IF PEEK(AD+8) AND BV THEN PV=PV+2\n      PXL(PX+CX*8,PY+CY*8)=PV\n    NEXT PX\n    AD=AD+1\n  NEXT PY\nEND SUB\n\nSUB BCLEAR\n  S=(SELSIZE+1)*8-1\n  FOR PY=0 TO S\n    FOR PX=0 TO S\n      PXL(PX,PY)=SELCOLOR\n    NEXT PX\n  NEXT PY\n  CALL WRITECHARSEL\n  CALL DRAWCHAR\n  CALL CHAR2PAGE\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BCUT\n  AD=USERCHARS+SELCHAR*16\n  S=(SELSIZE+1)*16\n  FOR I=0 TO SELSIZE\n    COPY AD+I*256,S TO TEMPMEM+I*S\n    FILL AD+I*256,S,0\n  NEXT I\n  CALL READCHAR\n  CALL DRAWCHAR\n  CALL CHAR2PAGE\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BCOPY\n  AD=USERCHARS+SELCHAR*16\n  S=(SELSIZE+1)*16\n  FOR I=0 TO SELSIZE\n    COPY AD+I*256,S TO TEMPMEM+I*S\n  NEXT I\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BPASTE\n  AD=USERCHARS+SELCHAR*16\n  S=(SELSIZE+1)*16\n  FOR I=0 TO SELSIZE\n    COPY TEMPMEM+I*S,S TO AD+I*256\n  NEXT I\n  CALL READCHAR\n  CALL DRAWCHAR\n  CALL CHAR2PAGE\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BFLIPX\n  S=(SELSIZE+1)*8-1\n  S2=(SELSIZE+1)*4-1\n  FOR Y=0 TO S\n    FOR X=0 TO S2\n      SWAP PXL(X,Y),PXL(S-X,Y)\n    NEXT X\n  NEXT Y\n  CALL WRITECHARSEL\n  CALL DRAWCHAR\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BFLIPY\n  S=(SELSIZE+1)*8-1\n  S2=(SELSIZE+1)*4-1\n  FOR X=0 TO S\n    FOR Y=0 TO S2\n      SWAP PXL(X,Y),PXL(X,S-Y)\n    NEXT Y\n  NEXT X\n  CALL WRITECHARSEL\n  CALL DRAWCHAR\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BSPIN\n  S=(SELSIZE+1)*8-1\n  S2=(SELSIZE+1)*4-1\n  FOR X=0 TO S\n    FOR Y=X+1 TO S\n      SWAP PXL(X,Y),PXL(Y,X)\n    NEXT Y\n  NEXT X\n  FOR X=0 TO S\n    FOR Y=0 TO S2\n      SWAP PXL(X,Y),PXL(X,S-Y)\n    NEXT Y\n  NEXT X\n  CALL WRITECHARSEL\n  CALL DRAWCHAR\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BMOVEU\n  S=(SELSIZE+1)*8-1\n  FOR Y=0 TO S-1\n    FOR X=0 TO S\n      SWAP PXL(X,Y),PXL(X,Y+1)\n    NEXT X\n  NEXT Y\n  CALL WRITECHARSEL\n  CALL DRAWCHAR\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BMOVED\n  S=(SELSIZE+1)*8-1\n  FOR Y=S TO 1 STEP -1\n    FOR X=0 TO S\n      SWAP PXL(X,Y),PXL(X,Y-1)\n    NEXT X\n  NEXT Y\n  CALL WRITECHARSEL\n  CALL DRAWCHAR\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BMOVEL\n  S=(SELSIZE+1)*8-1\n  FOR Y=0 TO S\n    FOR X=0 TO S-1\n      SWAP PXL(X,Y),PXL(X+1,Y)\n    NEXT X\n  NEXT Y\n  CALL WRITECHARSEL\n  CALL DRAWCHAR\n  CALL DRAWCHARNUM(0)\nEND SUB\n\nSUB BMOVER\n  S=(SELSIZE+1)*8-1\n  FOR Y=0 TO S\n    FOR X=S TO 1 STEP -1\n      SWAP PXL(X,Y),PXL(X-1,Y)\n    NEXT X\n  NEXT Y\n  CALL WRITECHARSEL\n  CALL DRAWCHAR\n  CALL DRAWCHARNUM(0)\nEND SUB\n\n'===================================\n\nBIGEDITOR:\n\nIF SELCHAR MOD 16>14 THEN SELCHAR=SELCHAR-1\nIF (SELCHAR MOD 64)\\16>2 THEN SELCHAR=SELCHAR-16\n\nCHAROFFSET=MIN(12,SELCHAR MOD 16)\nHIDECOUNT=0\nCALL READCHARBIG\nCALL DRAWUIBIG\n\nCALL RESETZONES\n\nZONE_PAL=-1\nCALL SETZONE(0,0,0,16,16)\nCALL SETZONE(1,17,3,2,8)\nCALL SETZONE(2,16,12,4,4)\n\nZONE_PAL=5\nCALL SETZONE(3,17,0,2,2)\n\nDO\n  CALL UPDATEZONES\n \n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n\n    IF CUR_ZONE=0 THEN\n      PXL(ZONE_IN_X,ZONE_IN_Y)=SELCOLOR\n      PAL SELPAL MOD 4\n      CELL ZONE_LAST_X,ZONE_LAST_Y,16+SELCOLOR\n      CALL WRITECHARBIG\n    END IF\n\n    IF CUR_ZONE=1 THEN\n      SELCOLOR=ZONE_IN_Y\\2\n      CALL DRAWSELCOLOR(17,3)\n    END IF\n\n    IF CUR_ZONE=2 THEN\n      SX=MAX(0,MIN(2,ZONE_IN_X-1))\n      SY=MAX(0,MIN(2,ZONE_IN_Y-1))\n      SELCHAR=(SY*16)+SX+SELPAGE*64+CHAROFFSET\n      CALL READCHARBIG\n      CALL DRAWCHARBIG\n      CALL DRAWCHARNUMBIG\n    END IF\n\n  END IF\n\n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=3 THEN GOTO CHAREDITOR\n  END IF\n\n  IF HIDECOUNT>0 THEN\n    HIDECOUNT=HIDECOUNT-1\n    IF HIDECOUNT=0 THEN CALL HIDESELCHAR\n  END IF\n\n  WAIT VBL\nLOOP\n\nSUB DRAWUIBIG\n  PAL 0\n  BG COPY 0,32,20,16 TO 0,0\n  CALL DRAWCHARBIG\n  CALL DRAWSELCOLOR(17,3)\n  CALL DRAWPAGEBIG\n  CALL DRAWCHARNUMBIG\n  P=(SELPAL MOD 4)\n  BG TINT 17,3 TO 18,10 PAL P\nEND SUB\n\nSUB DRAWCHARBIG\n  PAL SELPAL MOD 4\n  FOR PY=0 TO 15\n    FOR PX=0 TO 15\n      CELL PX,PY,16+PXL(PX,PY)\n    NEXT PX\n  NEXT PY\nEND SUB\n\nSUB READCHARBIG\n  CALL READCHARNUM(0,0)\n  CALL READCHARNUM(1,0)\n  CALL READCHARNUM(0,1)\n  CALL READCHARNUM(1,1)\nEND SUB\n\nSUB WRITECHARBIG\n  CALL WRITECHARNUM(0,0)\n  CALL WRITECHARNUM(1,0)\n  CALL WRITECHARNUM(0,1)\n  CALL WRITECHARNUM(1,1)\n  CALL CHAR2PAGENUM(SELCHAR)\n  CALL CHAR2PAGENUM(SELCHAR+1)\n  CALL CHAR2PAGENUM(SELCHAR+16)\n  CALL CHAR2PAGENUM(SELCHAR+17)\nEND SUB\n\nSUB DRAWPAGEBIG\n  PAL SELPAL MOD 4\n  CI=192+CHAROFFSET\n  FOR CY=12 TO 15\n    FOR CX=16 TO 19\n      CELL CX,CY,CI\n      INC CI\n    NEXT CX\n    ADD CI,12\n  NEXT CY\nEND SUB\n\nSUB DRAWCHARNUMBIG\n  N=SELCHAR MOD 64\n  SX=128+((N-CHAROFFSET) MOD 16)*8\n  SY=96+(N\\16)*8\n  SPRITE 0,SX-1,SY-1,20\n  SPRITE 1,SX+9,SY-1,20\n  SPRITE 2,SX-1,SY+9,20\n  SPRITE 3,SX+9,SY+9,20\n  HIDECOUNT=60\nEND SUB\n\n'======================\n\nPALEDITOR:\n\nCALL INITMEM\nCALL FIXSEL(-1)\nCALL READPAL\nCALL READPAGE\nCALL DRAWPALUI\n\nCALL RESETZONES\n\n' BAR BUTTONS\nZONE_PAL=5\nCALL SETZONE(0,0,14,2,2)\n' BAR TABS\nZONE_PAL=-1\nCALL SETZONE(7,14,14,2,2)\nCALL SETZONE(8,16,14,2,2)\nCALL SETZONE(9,18,14,2,2)\n\n' PAGE AREA\nZONE_PAL=-1\nCALL SETZONE(10,2,10,16,4)\n' PAGE UP/DOWN\nZONE_PAL=5\nCALL SETZONE(11,18,10,2,2)\nCALL SETZONE(12,18,12,2,2)\n' SELECTION SIZE\nCALL SETZONE(13,0,10,2,2)\nCALL SETZONE(14,0,12,2,2)\n\n' COLORS AREA\nZONE_PAL=-1\nCALL SETZONE(20,10,1,2,8)\n' PALETTES AREA\nCALL SETZONE(21,1,1,7,4)\n' RGB SLIDERS\nCALL SETZONE(22,13,1,2,4)\nCALL SETZONE(23,15,1,2,4)\nCALL SETZONE(24,17,1,2,4)\n' PALETTE UP/DOWN\nZONE_PAL=5\nCALL SETZONE(25,1,7,2,2)\nCALL SETZONE(26,6,7,2,2)\n\nDO\n  CALL UPDATEZONES\n  \n  K$=INKEY$\n  IF K$<>\"\" THEN\n    IF K$=\"1\" THEN GOTO CHAREDITOR\n    IF K$=\"3\" THEN GOTO MAPEDITOR\n  END IF\n\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=7 THEN GOTO CHAREDITOR\n    IF CUR_ZONE=9 THEN GOTO MAPEDITOR\n  END IF\n  \n  CALL HANDLECHARSEL(-1)\n\n  '** PAL **\n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n    IF CUR_ZONE=21 THEN\n      SELPAL=SELPALPAGE*4+(ZONE_IN_X\\4)+(ZONE_IN_Y\\2)*2\n      CALL DRAWPAL\n    END IF\n  END IF\n\n  '** PAL PAGE **\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=25 THEN\n      SELPALPAGE=0\n      SELPAL=SELPAL MOD 4\n      CALL READPAL\n      CALL DRAWPAL\n    END IF\n    IF CUR_ZONE=26 THEN\n      SELPALPAGE=1\n      SELPAL=(SELPAL MOD 4)+4\n      CALL READPAL\n      CALL DRAWPAL\n    END IF\n  END IF\n\n  '** COLOR **\n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n    IF CUR_ZONE=20 THEN\n      SELCOLOR=ZONE_IN_Y\\2\n      CALL DRAWSLIDERS\n      CALL DRAWSELCOLOR(10,1)\n    END IF\n  END IF\n\n  '** RGB SLIDERS **\n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n    IF CUR_ZONE>=22 AND CUR_ZONE<=24 THEN\n      V=3-ZONE_IN_Y\n      A=USERPALS+SELPAL*4+SELCOLOR\n      C=PEEK(A)\n      R=INT(C/16)\n      G=INT(C/4) MOD 4\n      B=C MOD 4\n      IF CUR_ZONE=22 THEN R=V\n      IF CUR_ZONE=23 THEN G=V\n      IF CUR_ZONE=24 THEN B=V\n      C=R*16+G*4+B\n      POKE A,C\n      CALL DRAWSLIDERS\n      CALL READPAL\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=0 THEN\n      FTYPE$=\"PALETTES\"\n      GOTO FILESMENU\n    END IF\n  END IF\n\n  WAIT VBL\nLOOP\n\nSUB DRAWPALUI\n  BG COPY 0,16,20,16 TO 0,0\n  PAL 4\n  TEXT 1,2,\"#\"\n  TEXT 5,2,\"#\"\n  TEXT 1,4,\"#\"\n  TEXT 5,4,\"#\"\n  TEXT 13,4,\"R\"\n  TEXT 15,4,\"G\"\n  TEXT 17,4,\"B\"\n  CALL INITSPRITES\n  FOR I=12 TO 14\n    SPRITE I PAL 7 SIZE 1\n  NEXT I\n  CALL DRAWCHARNUM(-1)\n  CALL DRAWPAGE\n  CALL DRAWPAL\n  CALL DRAWSELCOLOR(10,1)\nEND SUB\n\nSUB READPAL\n  COPY USERPALS+SELPALPAGE*16,16 TO $FF00\nEND SUB\n\nSUB DRAWPAL\n  PAL 4\n  NUMBER 3,8,SELPALPAGE+1,1\n  TEXT 4,8,\"/2\"\n  C=SELPALPAGE*4\n  NUMBER 2,2,C+0,1\n  NUMBER 6,2,C+1,1\n  NUMBER 2,4,C+2,1\n  NUMBER 6,4,C+3,1\n  CALL DRAWSLIDERS\n  P=(SELPAL MOD 4)\n  BG TINT 10,1 TO 11,8 PAL P\n  BG TINT 2,10 TO 17,13 PAL P\n  SX=8+(P MOD 2)*32\n  SY=8+(P\\2)*16\n  SPRITE 8,SX,SY,20\n  SPRITE 9,SX+16,SY,20\n  SPRITE 10,SX,SY+8,20\n  SPRITE 11,SX+16,SY+8,20\nEND SUB\n\nSUB DRAWSLIDERS\n  C=PEEK(USERPALS+SELPAL*4+SELCOLOR)\n  R=C\\16\n  G=C\\4 MOD 4\n  B=C MOD 4\n  SPRITE 12,13*8,(4-R)*8,32\n  SPRITE 13,15*8,(4-G)*8,32\n  SPRITE 14,17*8,(4-B)*8,32\nEND SUB\n\n'======================\n\nMAPEDITOR:\n\nSPRITE OFF\nCLS\nCOPY USERCHARS,$C00 TO $8000\nCOPY ROM(2)+$E00,$200 TO $8E00\nCOPY USERPALS,32 TO $FF00\nCALL FIXSEL(-1)\nCALL DRAWTOOLBAR\nCALL DRAWMAP\nON RASTER CALL MAPEDITORRASTER\n\nCALL RESETZONES\n\n' BAR BUTTONS\nZONE_PAL=1\nCALL SETZONE(0,0,14,2,2)\nZONE_PAL=-1\nCALL SETZONE(1,3,14,2,2)\nCALL SETZONE(2,5,14,2,2)\nCALL SETZONE(3,7,14,2,2)\nZONE_PAL=1\nCALL SETZONE(4,9,14,2,2)\nCALL SETZONE(5,11,14,2,2)\n' BAR TABS\nZONE_PAL=-1\nCALL SETZONE(7,14,14,2,2)\nCALL SETZONE(8,16,14,2,2)\nCALL SETZONE(9,18,14,2,2)\n\n' MAP AREA\nCALL SETZONE(10,0,0,20,14)\n\nIF CELLSIZE=0 THEN\n  WINW=20\n  WINH=14\nELSE\n  WINW=10\n  WINH=7\nEND IF\n\nOLDX=-1\nOLDY=-1\nFROMX=0\nFROMY=0\n\nDO\n  CALL UPDATEZONES\n  \n  IF MAPBARST>0 THEN\n    DEC MAPBARST\n    IF MAPBARST=0 THEN SPRITE OFF 0 TO 7\n  END IF\n\n  K$=INKEY$\n  IF K$<>\"\" THEN\n    IF K$=\"1\" THEN GOTO CHAREDITOR\n    IF K$=\"2\" THEN GOTO PALEDITOR\n    IF K$=\"Z\" THEN GOSUB SETSCROLLMODE\n    IF K$=\"X\" THEN GOSUB SETDRAWMODE\n    IF K$=\"C\" THEN GOSUB SETRUBBERMODE\n    IF K$=\"V\" THEN GOSUB TOGGLEFLIP\n    IF K$=\"B\" THEN GOSUB TOGGLEPRI\n\n    K=ASC(K$)\n    IF K=17 OR K$=\"D\" THEN\n      MAPX=MIN(MAPX+1,MAPW-WINW)\n      CALL DRAWMAP\n    ELSE IF K=18 OR K$=\"A\" THEN\n      MAPX=MAX(MAPX-1,0)\n      CALL DRAWMAP\n    ELSE IF K=19 OR K$=\"S\" THEN\n      MAPY=MIN(MAPY+1,MAPH-WINH)\n      CALL DRAWMAP\n    ELSE IF K=20 OR K$=\"W\" THEN\n      MAPY=MAX(MAPY-1,0)\n      CALL DRAWMAP\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=7 THEN GOTO CHAREDITOR\n    IF CUR_ZONE=8 THEN GOTO PALEDITOR\n    IF CUR_ZONE=1 THEN GOSUB SETSCROLLMODE\n    IF CUR_ZONE=2 THEN GOSUB SETDRAWMODE\n    IF CUR_ZONE=3 THEN GOSUB SETRUBBERMODE\n  END IF\n  \n  IF (ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG) AND CUR_ZONE=10 THEN\n      \n    IF CELLSIZE=0 THEN\n      X=ZONE_IN_X\n      Y=ZONE_IN_Y\n    ELSE\n      X=ZONE_IN_X\\2\n      Y=ZONE_IN_Y\\2\n    END IF\n\n    IF ZONE_EVENT=E_DOWN THEN\n      OLDX=-1\n      FROMX=X\n      FROMY=Y\n    END IF\n\n    IF X<>OLDX OR Y<>OLDY THEN\n\n      '** SCROLL MODE **\n      IF EDMODE=0 THEN\n        IF OLDX>=0 THEN\n          MAPX=MIN(MAX(MAPX-X+DRAGX,0),MAPW-WINW)\n          MAPY=MIN(MAX(MAPY-Y+DRAGY,0),MAPH-WINH)\n          CALL DRAWMAP\n        END IF\n        DRAGX=X\n        DRAGY=Y\n      END IF\n\n      '** DRAW CHAR **\n      IF EDMODE=1 AND TOOLMODE=0 THEN\n        CALL MAPCHAR(X,Y)\n      END IF\n\n      '** DRAW PALETTE **\n      IF EDMODE=1 AND TOOLMODE=1 THEN\n        CALL MAPPAL(X,Y)\n      END IF\n\n      '** DRAW PRIO **\n      IF EDMODE=1 AND TOOLMODE=2 THEN\n        CALL MAPPRI(X,Y)\n      END IF\n\n      '** FILL CHAR **\n      IF EDMODE=1 AND TOOLMODE=3 THEN\n        IF Y>FROMY THEN SY=SELSIZE+1 ELSE SY=-SELSIZE-1\n        IF X>FROMX THEN SX=SELSIZE+1 ELSE SX=-SELSIZE-1\n        FOR CY=FROMY TO Y STEP SY\n          FOR CX=FROMX TO X STEP SX\n            CALL MAPCHAR(CX,CY)\n          NEXT CX\n        NEXT CY\n      END IF\n\n      '** FILL PALETTE **\n      IF EDMODE=1 AND TOOLMODE=4 THEN\n        IF Y>FROMY THEN SY=1 ELSE SY=-1\n        IF X>FROMX THEN SX=1 ELSE SX=-1\n        FOR CY=FROMY TO Y STEP SY\n          FOR CX=FROMX TO X STEP SX\n            CALL MAPPAL(CX,CY)\n          NEXT CX\n        NEXT CY\n      END IF\n\n      '** FILL PRIO **\n      IF EDMODE=1 AND TOOLMODE=5 THEN\n        IF Y>FROMY THEN SY=1 ELSE SY=-1\n        IF X>FROMX THEN SX=1 ELSE SX=-1\n        FOR CY=FROMY TO Y STEP SY\n          FOR CX=FROMX TO X STEP SX\n            CALL MAPPRI(CX,CY)\n          NEXT CX\n        NEXT CY\n      END IF\n\n      '** SELECT STAMP **\n      IF EDMODE=1 AND TOOLMODE=6 THEN\n        CALL MAPDRAWSEL(FROMX,FROMY,X,Y)\n      END IF\n\n      '** USE STAMP **\n      IF EDMODE=1 AND TOOLMODE=7 THEN\n        CALL MAPSTAMP(X,Y)\n      END IF\n\n      '** RUBBER MODE **\n      IF EDMODE=2 THEN\n        A=USERBG+4+((Y+MAPY)*MAPW+(X+MAPX))*2\n        POKEW A,0\n        A=$9000+(Y*32+X)*2\n        POKEW A,0\n      END IF\n\n      OLDX=X\n      OLDY=Y\n    END IF\n\n  END IF\n\n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=0 THEN\n      FTYPE$=\"BACKGROUND\"\n      GOTO FILESMENU\n    END IF\n    IF CUR_ZONE=4 THEN GOSUB TOGGLEFLIP\n    IF CUR_ZONE=5 THEN GOSUB TOGGLEPRI\n    IF CUR_ZONE=10 THEN\n      IF EDMODE=1 AND TOOLMODE=6 THEN\n        SPRITE OFF 0 TO 3\n        CALL MAPSTAMPCOPY(MAPX+FROMX,MAPY+FROMY,MAPX+X,MAPY+Y)\n        TOOLMODE=7\n        CALL DRAWTOOLBAR\n      END IF\n    END IF\n  END IF\n\n  WAIT VBL\nLOOP\n\nSETSCROLLMODE:\n  EDMODE=0\n  CALL DRAWEDMODE\nRETURN\n\nSETDRAWMODE:\n  IF EDMODE=1 THEN\n    RETURN MAPTOOLSMENU\n  ELSE\n    EDMODE=1\n    CALL DRAWEDMODE\n  END IF\nRETURN\n\nSETRUBBERMODE:\n  EDMODE=2\n  CALL DRAWEDMODE\nRETURN\n\nTOGGLEFLIP:\n  ADD FLIPMODE,1,0 TO 3\n  CALL DRAWTOOLBAR\nRETURN\n\nTOGGLEPRI:\n  ADD PRI,1,0 TO 1\n  CALL DRAWTOOLBAR\nRETURN\n\nSUB MAPCHAR(X,Y)\n  W=MIN(SELSIZE,WINW-1-X)\n  H=MIN(SELSIZE,WINH-1-Y)\n  FOR I=0 TO H\n    FOR J=0 TO W\n      IF CELLSIZE=0 THEN\n        C=SELCHAR+I*16+J\n      ELSE\n        C=SELCHAR+I*2*16+J*2\n      END IF\n      ATT=SELPAL+FLIPMODE*8+PRI*32\n      IF FLIPMODE AND %01 THEN CX=X+W-J ELSE CX=X+J\n      IF FLIPMODE AND %10 THEN CY=Y+H-I ELSE CY=Y+I\n      A=$9000+(CY*32+CX)*2\n      POKE A,C\n      POKE A+1,ATT\n      A=USERBG+4+((CY+MAPY)*MAPW+(CX+MAPX))*2\n      POKE A,C\n      POKE A+1,ATT\n    NEXT J\n  NEXT I\nEND SUB\n\nSUB MAPPAL(X,Y)\n  A=USERBG+4+((Y+MAPY)*MAPW+(X+MAPX))*2+1\n  POKE A,(PEEK(A) AND %11111000) OR SELPAL\n  A=$9000+(Y*32+X)*2+1\n  POKE A,(PEEK(A) AND %11111000) OR SELPAL\nEND SUB\n\nSUB MAPPRI(X,Y)\n  'WRITE TO DATA...\n  A=USERBG+4+((Y+MAPY)*MAPW+(X+MAPX))*2+1\n  ATT=PEEK(A) AND %11011111\n  POKE A,ATT OR PRI*32\n  'WRITE TO SCREEN...\n  A=$9000+(Y*32+X)*2+1\n  ATT=PEEK(A) AND %11011000\n  POKE A,ATT OR PRI*32 OR PRI\nEND SUB\n\nSUB MAPSTAMPCOPY(X1,Y1,X2,Y2)\n  XL=MIN(X1,X2)\n  XR=MAX(X1,X2)\n  YT=MIN(Y1,Y2)\n  YB=MAX(Y1,Y2)\n  STAMPW=XR-XL\n  STAMPH=YB-YT\n  FOR I=0 TO STAMPH\n    SRC=USERBG+4+((YT+I)*MAPW+XL)*2\n    TAR=TEMPMEM+I*(STAMPW+1)*2\n    COPY SRC,(STAMPW+1)*2 TO TAR\n  NEXT I\nEND SUB\n\nSUB MAPSTAMP(X,Y)\n  W=MIN(STAMPW,WINW-1-X)\n  H=MIN(STAMPH,WINH-1-Y)\n  FOR I=0 TO H\n    FOR J=0 TO W\n      A=TEMPMEM+(I*(STAMPW+1)+J)*2\n      V=PEEKW(A)\n      A=USERBG+4+((Y+MAPY+I)*MAPW+(X+MAPX+J))*2\n      POKEW A,V\n      A=$9000+((Y+I)*32+X+J)*2\n      POKEW A,V\n    NEXT J\n  NEXT I\nEND SUB\n\nSUB MAPEDITORRASTER\n  IF RASTER=0 THEN\n    CELL SIZE 0,CELLSIZE\n    IF EDMODE=1 AND (TOOLMODE=2 OR TOOLMODE=5) THEN\n      PALETTE 0,%000000,%110000,%100000,%010000\n      PALETTE 1,%000000,%001100,%001000,%000100\n    ELSE\n      COPY USERPALS,8 TO $FF00\n    END IF\n  END IF\n  IF RASTER=112 THEN\n    CELL SIZE 0,0\n    PALETTE 0,%000000,%111111,%101010,%010101\n    PALETTE 1,%000000,%001111,%000101,%000000\n  END IF\nEND SUB\n\nSUB DRAWEDMODE\n  COPY USERPALS,32 TO $FF00\n  CALL DRAWMAP\n  CALL DRAWTOOLBAR\nEND SUB\n\nSUB DRAWMAP\n  BG SOURCE USERBG\n  BG COPY MAPX,MAPY,20,14 TO 0,0\n  IF EDMODE=1 AND (TOOLMODE=2 OR TOOLMODE=5) THEN CALL DRAWPRIO\n  CALL MAPBARS\nEND SUB\n\nSUB DRAWPRIO\n  FOR CY=0 TO 13\n    FOR CX=0 TO 19\n      A=$9000+(CY*32+CX)*2+1\n      ATT=PEEK(A) AND %11111000\n      IF ATT AND %00100000 THEN ATT=ATT OR 1\n      POKE A,ATT\n    NEXT CX\n  NEXT CY\nEND SUB\n\nSUB DRAWTOOLBAR\n  PAL 0\n  BG FILL 0,14 TO 19,14 CHAR 224\n  BG FILL 0,15 TO 19,15 CHAR 240\n  CELL 0,14,230\n  CELL 1,14,231\n  CELL 0,15,246\n  CELL 1,15,247\n  CALL DRAWTOOLBUTTON(3,225,229,EDMODE=0,0)\n  CALL DRAWTOOLBUTTON(5,238,239,EDMODE=1,-1)\n  CALL DRAWTOOLBUTTON(7,225,252,EDMODE=2,0)\n  CALL DRAWFLAGBUTTON(9,232,233,248+FLIPMODE)\n  CALL DRAWFLAGBUTTON(11,234,235,236+PRI)\n  CALL DRAWTOOLBUTTON(14,225,226,0,0)\n  CALL DRAWTOOLBUTTON(16,225,227,0,0)\n  CALL DRAWTOOLBUTTON(18,225,228,-1,0)\n  IF TOOLMODE=6 THEN\n    CALL COPYCHAR(225,238)\n    CALL COPYCHAR(195,239)\n  ELSE IF TOOLMODE=7 THEN\n    CALL COPYCHAR(225,238)\n    CALL COPYCHAR(196,239)\n  ELSE\n    CALL COPYCHAR(208+TOOLMODE\\3,238)\n    CALL COPYCHAR(192+(TOOLMODE MOD 3),239)\n  END IF\nEND SUB\n\nSUB DRAWTOOLBUTTON(X,ICON1,ICON2,SEL,MENU)\n  CELL X,14,ICON1\n  CELL X+1,14,ICON2\n  IF SEL THEN\n    CELL X,15,241\n    IF MENU THEN CELL X+1,15,245 ELSE CELL X+1,15,242\n  ELSE\n    CELL X,15,243\n    CELL X+1,15,244\n  END IF\nEND SUB\n\nSUB DRAWFLAGBUTTON(X,C1,C2,C3)\n  CELL X,14,C1\n  CELL X+1,14,C2\n  CELL X,15,246\n  CELL X+1,15,C3\nEND SUB\n\nSUB COPYCHAR(SRC,DST)\n  COPY ROM(2)+SRC*16,16 TO $8000+DST*16\nEND SUB\n\nSUB MAPBARS\n  IF CELLSIZE=0 THEN\n    WINW=20\n    WINH=14\n    CS=8\n  ELSE\n    WINW=10\n    WINH=7\n    CS=16\n  END IF\n  \n  SPRITE 0 PAL 0 FLIP 0,0 PRIO 1\n  SPRITE 1 PAL 0 FLIP 1,0 PRIO 1\n  SPRITE 2 PAL 0 FLIP 0,0 PRIO 1\n  SPRITE 3 PAL 0 FLIP 0,1 PRIO 1\n  BX=MAPX/MAPW*154\n  BW=WINW*154/MAPW\n  BY=MAPY/MAPH*105\n  BH=WINH*105/MAPH\n  SPRITE 0,BX-1,0,253\n  SPRITE 1,BW+BX-1,0,253\n  SPRITE 2,0,BY-1,254\n  SPRITE 3,0,BH+BY-1,254\n  \n  SPRITE 4 PAL 0 PRIO 1\n  SPRITE 5 PAL 0 PRIO 1\n  SPRITE 6 PAL 0 PRIO 1\n  SPRITE 7 PAL 0 PRIO 1\n  BX=-((MAPX*CS) MOD 160)-4\n  BY=-((MAPY*CS) MOD 128)-4\n  IF BX>-32 THEN\n    SPRITE 4,BX,BY,255\n    SPRITE 5,BX,BY+128,255\n  ELSE\n    SPRITE OFF 4 TO 5\n  END IF\n  SPRITE 6,BX+160,BY,255\n  SPRITE 7,BX+160,BY+128,255\n  \n  MAPBARST=60\nEND SUB\n\nSUB MAPDRAWSEL(X1,Y1,X2,Y2)\n  XL=MIN(X1,X2)\n  XR=MAX(X1,X2)\n  YT=MIN(Y1,Y2)\n  YB=MAX(Y1,Y2)\n  SPRITE 0 PAL 0 FLIP 0,0 PRIO 1\n  SPRITE 1 PAL 0 FLIP 1,0 PRIO 1\n  SPRITE 2 PAL 0 FLIP 0,1 PRIO 1\n  SPRITE 3 PAL 0 FLIP 1,1 PRIO 1\n  SPRITE 0,XL*8-1,YT*8-1,253\n  SPRITE 1,XR*8+1,YT*8-1,253\n  SPRITE 2,XL*8-1,YB*8+1,253\n  SPRITE 3,XR*8+1,YB*8+1,253\nEND SUB\n\nMAPTOOLSMENU:\nCALL INITMEM\nMENU$(0)=\"DRAW CHARACTER\"\nMENU$(1)=\"DRAW PALETTE\"\nMENU$(2)=\"DRAW PRIORITY\"\nMENU$(3)=\"FILL CHARACTER\"\nMENU$(4)=\"FILL PALETTE\"\nMENU$(5)=\"FILL PRIORITY\"\nMENU$(6)=\"STAMP\"\nCALL SHOWMENU(\"SELECT TOOL\",6,R)\nTOOLMODE=R\nGOTO MAPEDITOR\n\n'======================\n\nSIZEMENU:\nCALL INITMEM\nGLOBAL NEWW,NEWH,NEWC\nNEWW=MAPW\nNEWH=MAPH\nNEWC=CELLSIZE\nCALL DRAWSIZEMENU\nCALL DRAWSIZE\n\nCALL RESETZONES\n\nZONE_PAL=-1\nCALL SETZONE(0,1,4,5,6)\nCALL SETZONE(1,7,4,5,6)\nCALL SETZONE(2,14,4,5,2)\n\nZONE_PAL=5\nCALL SETZONE(3,0,14,10,2)\nCALL SETZONE(4,10,14,10,2)\n\nDO\n  CALL UPDATEZONES\n  \n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n    IF CUR_ZONE=0 THEN\n      NEWW=SIZESW(ZONE_IN_Y)\n      CALL DRAWSIZE\n    END IF\n    IF CUR_ZONE=1 THEN\n      NEWH=SIZESH(ZONE_IN_Y)\n      CALL DRAWSIZE\n    END IF\n    IF CUR_ZONE=2 THEN\n      NEWC=ZONE_IN_Y\n      CALL DRAWSIZE\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=3 THEN\n      IF NEWW*NEWH*2<=8192 AND (NEWC=1 OR (NEWW>=20 AND NEWH>=16)) THEN\n        CALL BCHANGESIZE\n        GOTO MAPEDITOR\n      END IF\n    END IF\n    IF CUR_ZONE=4 THEN GOTO FILESMENU\n  END IF\n\n  WAIT VBL\nLOOP\n\nSUB DRAWSIZEMENU\n  BG SOURCE ROM(5)\n  BG COPY 0,0,20,16 TO 0,0\n  PAL 5\n  TEXT 0,0,\"BACKGROUND SIZE     \"\n  PAL 4\n  TEXT 1,2,\"WIDTH\"\n  FOR I=0 TO 5\n    TEXT 1,4+I,STR$(SIZESW(I))\n  NEXT I\n  TEXT 7,2,\"HEIGHT\"\n  FOR I=0 TO 5\n    TEXT 7,4+I,STR$(SIZESH(I))\n  NEXT I\n  TEXT 14,2,\"CELL\"\n  TEXT 14,4,\"8X8\"\n  TEXT 14,5,\"16X16\"\n  TEXT 4,15,\"OK\"\n  TEXT 12,15,\"CANCEL\"\nEND SUB\n\nSUB DRAWSIZE\n  BG TINT 0,4 TO 19,12 PAL 4\n  'WIDTH\n  FOR I=0 TO 5\n    IF NEWW=SIZESW(I) THEN BG TINT 1,4+I TO 5,4+I PAL 5\n  NEXT I\n  'HEIGHT\n  FOR I=0 TO 5\n    IF NEWH=SIZESH(I) THEN BG TINT 7,4+I TO 11,4+I PAL 5\n  NEXT I\n  'CELL\n  BG TINT 14,4+NEWC TO 18,4+NEWC PAL 5\n  PAL 4\n  IF NEWW*NEWH*2>8192 THEN\n    TEXT 1,11,\"TOO BIG\"\n  ELSE IF NEWC=0 AND (NEWW<20 OR NEWH<16) THEN\n    TEXT 1,11,\"TOO SMALL\"\n  ELSE\n    TEXT 1,11,\"         \"\n  END IF\nEND SUB\n\nSUB BCHANGESIZE\n  IF NEWW>MAPW THEN\n    FOR Y=NEWH-1 TO 0 STEP -1\n      S=USERBG+4+Y*MAPW*2\n      D=USERBG+4+Y*NEWW*2\n      N=MAPW*2\n      COPY S,N TO D\n      FILL D+N,(NEWW-MAPW)*2\n    NEXT Y\n  ELSE IF NEWW<MAPW THEN\n    FOR Y=0 TO NEWH-1\n      COPY USERBG+4+Y*MAPW*2,NEWW*2 TO USERBG+4+Y*NEWW*2\n    NEXT Y\n  END IF\n  IF NEWH>MAPH THEN FILL USERBG+4+MAPH*NEWW*2,(NEWH-MAPH)*NEWW*2\n  MAPW=NEWW\n  MAPH=NEWH\n  CELLSIZE=NEWC\n  CALL UPDATEMAPINFO\n  MAPX=0\n  MAPY=0\n  CALL SHOWMESSAGE(\"SIZE CHANGED\")\nEND SUB\n\n'======================\n\nFILESMENU:\n\nCALL INITMEM\nCALL FILE2SEL\nFILES\nCALL DRAWFILESMENU\n\nCALL RESETZONES\n\nZONE_PAL=-1\nCALL SETZONE(0,0,2,18,11)\n\nZONE_PAL=5\nCALL SETZONE(1,18,2,2,2)\nCALL SETZONE(2,18,11,2,2)\nCALL SETZONE(10,0,14,2,2)\nIF FTYPE$=\"CHARACTERS\" THEN\n  CALL SETZONE(15,10,14,2,2)\nEND IF\nIF FTYPE$=\"CHARACTERS\" OR FTYPE$=\"BACKGROUND\" THEN\n  CALL SETZONE(16,12,14,2,2)\nEND IF\nCALL SETZONE(17,14,14,2,2)\nCALL SETZONE(18,16,14,2,2)\nCALL SETZONE(19,18,14,2,2)\n\nDO\n  CALL UPDATEZONES\n  \n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n    IF CUR_ZONE=0 THEN\n      SELFILE=ZONE_IN_Y+FILESOFFS\n      CALL DRAWFILES\n      CALL SEL2FILE\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=1 THEN\n      FILESOFFS=0\n      CALL DRAWFILES\n    END IF\n    IF CUR_ZONE=2 THEN\n      FILESOFFS=5\n      CALL DRAWFILES\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=10 THEN GOTO EXITFILES\n    IF FTYPE$=\"BACKGROUND\" THEN\n      IF CUR_ZONE=16 THEN GOTO SIZEMENU\n    END IF\n    IF FTYPE$=\"CHARACTERS\" THEN\n      IF CUR_ZONE=15 THEN\n        CALL BLOADPAGE(OK)\n        IF OK THEN GOTO EXITFILES\n      END IF\n      IF CUR_ZONE=16 THEN\n        MENU$(0)=\"NORMAL\"\n        MENU$(1)=\"OPAQUE\"\n        MENU$(2)=\"OUTLINE\"\n        MENU$(3)=\"SHADOW\"\n        MENU$(4)=\"CANCEL\"\n        CALL SHOWMENU(\"LOAD FONT?\",4,R)\n        IF R=4 THEN\n          CALL DRAWFILESMENU\n        ELSE\n          COPY ROM(11)+R*$400,$400 TO $A000+SELPAGE*$400\n          GOTO CHAREDITOR\n        END IF\n      END IF\n    END IF\n    IF CUR_ZONE=17 THEN\n      MENU$(0)=\"CLEAR\"\n      MENU$(1)=\"CANCEL\"\n      CALL SHOWMENU(\"CLEAR \"+FTYPE$+\"?\",1,R)\n      IF R=0 THEN\n        IF FTYPE$=\"PALETTES\" THEN CALL BNEWPAL\n        IF FTYPE$=\"CHARACTERS\" THEN CALL BNEWCHAR\n        IF FTYPE$=\"BACKGROUND\" THEN CALL BNEWBG\n        GOTO EXITFILES\n      ELSE\n        CALL DRAWFILESMENU\n      END IF\n    END IF\n    IF CUR_ZONE=18 THEN\n      CALL BLOAD(OK)\n      IF OK THEN GOTO EXITFILES\n    END IF\n    IF CUR_ZONE=19 THEN\n      CALL BSAVE(OK)\n      IF OK THEN GOTO EXITFILES\n    END IF\n  END IF\n\n  WAIT VBL\nLOOP\n\nEXITFILES:\nIF FTYPE$=\"PALETTES\" THEN GOTO PALEDITOR\nIF FTYPE$=\"CHARACTERS\" THEN GOTO CHAREDITOR\nIF FTYPE$=\"BACKGROUND\" THEN GOTO MAPEDITOR\nEND\n\nSUB DRAWFILESMENU\n  BG SOURCE ROM(4)\n  BG COPY 0,0,20,14 TO 0,0\n  BARY=14\n  IF FTYPE$=\"CHARACTERS\" THEN BARY=14\n  IF FTYPE$=\"PALETTES\" THEN BARY=16\n  IF FTYPE$=\"BACKGROUND\" THEN BARY=18\n  BG COPY 0,BARY,20,2 TO 0,14\n  PAL 5\n  TEXT 0,0,FTYPE$+\" FILE\"\n  SPRITE OFF\n  CALL DRAWFILES\nEND SUB\n\nSUB DRAWFILES\n  FOR I=0 TO 10\n    N=I+FILESOFFS\n    IF N=SELFILE THEN PAL 5 ELSE PAL 4\n    NUMBER 0,2+I,N,2\n    TEXT 2,2+I,\":               \"\n    TEXT 3,2+I,LEFT$(FILE$(N),15)\n  NEXT I\nEND SUB\n\nSUB FILE2SEL\n  IF FTYPE$=\"PALETTES\" THEN SELFILE=PALFILE\n  IF FTYPE$=\"CHARACTERS\" THEN SELFILE=CHARFILE\n  IF FTYPE$=\"BACKGROUND\" THEN SELFILE=BGFILE\nEND SUB\n\nSUB SEL2FILE\n  IF FTYPE$=\"PALETTES\" THEN PALFILE=SELFILE\n  IF FTYPE$=\"CHARACTERS\" THEN CHARFILE=SELFILE\n  IF FTYPE$=\"BACKGROUND\" THEN BGFILE=SELFILE\nEND SUB\n\nSUB BNEWCHAR\n  FILL USERCHARS,$1000,0\n  CHARFILE=-1\n  SELPAGE=0\n  SELCHAR=1\nEND SUB\n\nSUB BNEWPAL\n  COPY ROM(10),SIZE(10) TO USERPALS\n  PALFILE=-1\n  SELPALPAGE=0\n  SELPAL=0\nEND SUB\n\nSUB BNEWBG\n  FILL USERBG,$2004,0\n  CALL UPDATEMAPINFO\n  MAPX=0\n  MAPY=0\n  BGFILE=-1\nEND SUB\n\nSUB BLOAD(OK)\n  OK=0\n  IF SELFILE=-1 THEN\n    CALL SHOWMESSAGE(\"SELECT FILE\")\n    EXIT SUB\n  END IF\n  IF FTYPE$=\"PALETTES\" THEN CALL BLOADPAL(OK)\n  IF FTYPE$=\"CHARACTERS\" THEN CALL BLOADCHR(OK)\n  IF FTYPE$=\"BACKGROUND\" THEN CALL BLOADBG(OK)\n  IF OK THEN\n    CALL SHOWMESSAGE(FTYPE$+\" LOADED\")\n  ELSE\n    SELFILE=-1\n    CALL DRAWFILESMENU\n    CALL SHOWMESSAGE(\"INVALID FORMAT\")\n  END IF\nEND SUB\n\nSUB BSAVE(OK)\n  OK=0\n  IF SELFILE=-1 THEN\n    CALL SHOWMESSAGE(\"SELECT FILE\")\n    EXIT SUB\n  END IF\n  IF FTYPE$=\"PALETTES\" THEN CALL BSAVEPAL\n  IF FTYPE$=\"CHARACTERS\" THEN CALL BSAVECHR\n  IF FTYPE$=\"BACKGROUND\" THEN CALL BSAVEBG\n  CALL DRAWFILES\n  CALL SHOWMESSAGE(FTYPE$+\" SAVED\")\n  OK=-1\nEND SUB\n\nSUB BLOADPAGE(OK)\n  OK=0\n  IF SELFILE=-1 THEN\n    CALL SHOWMESSAGE(\"SELECT FILE\")\n    EXIT SUB\n  END IF\n  MENU$(0)=\"PAGE 1\"\n  MENU$(1)=\"PAGE 2\"\n  MENU$(2)=\"PAGE 3\"\n  MENU$(3)=\"PAGE 4\"\n  MENU$(4)=\"CANCEL\"\n  R=0\n  CALL SHOWMENU(\"LOAD ONLY FROM\",4,R)\n  IF R=4 THEN\n    CALL DRAWFILESMENU\n  ELSE\n    LOAD SELFILE,USERCHARS+SELPAGE*$400,$400,R*$400\n    CALL SHOWMESSAGE(\"PAGE LOADED\")\n    OK=-1\n  END IF\nEND SUB\n\n'======================\n\nSUB BLOADCHR(OK)\n  OK=0\n  IF FSIZE(CHARFILE)>$1000 THEN\n    CHARFILE=-1\n    EXIT SUB\n  END IF\n  FILL USERCHARS,$1000,0\n  LOAD CHARFILE,USERCHARS\n  OK=-1\nEND SUB\n\nSUB BSAVECHR\n  'FIND LAST USED CHARACTER\n  N=0\n  I=255\n  REPEAT\n    A=USERCHARS+I*16\n    FOR J=0 TO 15\n      IF PEEK(A+J)<>0 THEN N=I+1\n    NEXT J\n    I=I-1\n    IF I<0 THEN N=1\n  UNTIL N<>0\n  F$=FILE$(CHARFILE)\n  IF F$=\"\" THEN F$=\"CHARACTERS\"\n  SAVE CHARFILE,F$,USERCHARS,N*16\nEND SUB\n\nSUB BLOADPAL(OK)\n  OK=0\n  IF FSIZE(PALFILE)>32 THEN\n    PALFILE=-1\n    EXIT SUB\n  END IF\n  LOAD PALFILE,USERPALS\n  OK=-1\nEND SUB\n\nSUB BSAVEPAL\n  F$=FILE$(PALFILE)\n  IF F$=\"\" THEN F$=\"PALETTES\"\n  SAVE PALFILE,F$,USERPALS,32\nEND SUB\n\nSUB UPDATEMAPINFO\n  POKE USERBG,0\n  POKE USERBG+1,CELLSIZE\n  POKE USERBG+2,MAPW\n  POKE USERBG+3,MAPH\nEND SUB\n\nSUB RESETBG\n  FILL USERBG,$2004,0\n  MAPW=20\n  MAPH=16\n  MAPX=0\n  MAPY=0\n  CELLSIZE=0\n  SELCHAR=1\n  CALL UPDATEMAPINFO\nEND SUB\n\nSUB BLOADBG(OK)\n  OK=0\n  CALL RESETBG\n  IF FSIZE(BGFILE)=0 THEN\n    OK=-1\n    EXIT SUB\n  END IF\n  IF FSIZE(BGFILE)<4 OR FSIZE(BGFILE)>$2004 THEN\n    BGFILE=-1\n    EXIT SUB\n  END IF\n  LOAD BGFILE,USERBG\n  T=PEEK(USERBG)\n  CELLSIZE=PEEK(USERBG+1)\n  MAPW=PEEK(USERBG+2)\n  MAPH=PEEK(USERBG+3)\n  MAPX=0\n  MAPY=0\n  IF CELLSIZE=0 THEN SELCHAR=1 ELSE SELCHAR=2\n  'CHECK FILE FORMAT\n  IF T=0 AND CELLSIZE<2 AND MAPW>0 AND MAPH>0 THEN\n    OK=-1\n  ELSE\n    BGFILE=-1\n    CALL RESETBG\n  END IF\nEND SUB\n\nSUB BSAVEBG\n  F$=FILE$(BGFILE)\n  IF F$=\"\" THEN F$=\"BG\"\n  SAVE BGFILE,F$,USERBG,4+MAPW*MAPH*2\nEND SUB\n\n'==== GENERIC UI ====\n\nSUB SHOWMESSAGE(MSG$)\n  COPY $9000,$800 TO TEMPMEM\n  PAL 5\n  BG FILL 0,0 TO 19,0 CHAR 128\n  TEXT 0,0,MSG$\n  WAIT 60\n  COPY TEMPMEM,$800 TO $9000\nEND SUB\n\nSUB SHOWMENU(MSG$,MAXI,RESULT)\n  PAL 5\n  PRIO 1\n  BG FILL 0,0 TO 19,0 CHAR 128\n  TEXT (20-LEN(MSG$))/2,0,MSG$\n  PAL 4\n  Y=3+MAXI*2\n  BG FILL 0,1 TO 19,Y CHAR 1\n  BG FILL 0,1 TO 19,1 CHAR 3\n  BG FILL 0,Y TO 19,Y CHAR 5\n  FOR I=0 TO MAXI\n    T$=MENU$(I)\n    TEXT (20-LEN(T$))/2,2+I*2,T$\n  NEXT I\n  WHILE TOUCH\n    WAIT VBL\n  WEND\n  DO\n    IF TOUCH AND TOUCH.Y>=12 THEN\n      I=(((TOUCH.Y-4)\\8)-1)\\2\n      IF I<=MAXI THEN\n        CALL HIGHLIGHT(0,2+I*2,19,2+I*2)\n        WHILE TOUCH\n          WAIT VBL\n        WEND\n        RESULT=I\n        EXIT\n      END IF\n    END IF\n    WAIT VBL\n  LOOP\n  PRIO 0\nEND SUB\n\nSUB HIGHLIGHT(X1,Y1,X2,Y2)\n  BG TINT X1,Y1 TO X2,Y2 PAL 5\nEND SUB\n\n'==== TOUCH ZONES SUBPROGRAMS ====\n\nSUB SETZONE(N,X,Y,W,H)\n  ZONEX(N)=X\n  ZONEY(N)=Y\n  ZONEW(N)=W\n  ZONEH(N)=H\n  IF ZONE_PAL>=0 AND ZONE_PAL<8 THEN\n    'HIGHLIGHT AND NORMAL PALETTE\n    P=CELL.A(X,Y) AND %111\n    ZONEP(N)=(ZONE_PAL*16)+P\n  ELSE\n    'NO HIGHLIGHT\n    ZONEP(N)=$FF\n  END IF\nEND SUB\n\nSUB CLEARZONE(N)\n  CALL SETZONE(N,0,0,0,0)\nEND SUB\n\nSUB RESETZONES\n  FOR I=0 TO MAX_ZONE\n    CALL CLEARZONE(I)\n  NEXT I\n  CUR_ZONE=-1\n  ZONE_PAL=-1\nEND SUB\n\nSUB UPDATEZONES\n  CX=TOUCH.X\\8\n  CY=TOUCH.Y\\8\n  IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN CUR_ZONE=-1\n  ZONE_EVENT=0\n  IF CUR_ZONE>=0 THEN\n    CALL INSIDEZONE(CUR_ZONE,CX,CY)\n    IF ZONE_RESULT THEN\n      ZONE_IN_X=CX-ZONEX(CUR_ZONE)\n      ZONE_IN_Y=CY-ZONEY(CUR_ZONE)\n      IF NOT TOUCH THEN\n        CALL PAINTZONE(CUR_ZONE,0)\n        ZONE_EVENT=E_UP\n      ELSE IF CX<>ZONE_LAST_X OR CY<>ZONE_LAST_Y THEN\n        ZONE_EVENT=E_DRAG\n      END IF\n    ELSE\n      CALL PAINTZONE(CUR_ZONE,0)\n      ZONE_EVENT=E_OUT\n    END IF\n  ELSE IF TAP THEN\n    FOR I=0 TO MAX_ZONE\n      IF ZONEW(I)>0 THEN\n        CALL INSIDEZONE(I,CX,CY)\n        IF ZONE_RESULT THEN\n          ZONE_EVENT=E_DOWN\n          CUR_ZONE=I\n          ZONE_IN_X=CX-ZONEX(I)\n          ZONE_IN_Y=CY-ZONEY(I)\n          CALL PAINTZONE(I,1)\n        END IF\n      END IF\n    NEXT I\n  END IF\n  ZONE_LAST_X=CX\n  ZONE_LAST_Y=CY\nEND SUB\n\nSUB INSIDEZONE(N,CX,CY)\n  ZONE_RESULT=(CX>=ZONEX(N) AND CX<ZONEX(N)+ZONEW(N) AND CY>=ZONEY(N) AND CY<ZONEY(N)+ZONEH(N))\nEND SUB\n\nSUB PAINTZONE(N,SEL)\n  P=ZONEP(N)\n  IF P<>$FF THEN\n    IF SEL THEN P=P\\16 ELSE P=P MOD 16\n    BG TINT ZONEX(N),ZONEY(N) TO ZONEX(N)+ZONEW(N)-1,ZONEY(N)+ZONEH(N)-1 PAL P\n  END IF\nEND SUB\n\n'======================\n\n#1:MAIN PALETTES\n003F2F0000383400003C0C00003F3C00\n003F2A15000F050000300000003F2A15\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n0000000000000000FFFFFFFFFFFFFFFF\n0101010101010101FFFFFFFFFFFFFFFF\nFF0000000000000000FFFFFFFFFFFFFF\n80808080808080807F7F7F7F7F7F7F7F\n00000000000000FFFFFFFFFFFFFFFFFF\nFF000000000000FF00FFFFFFFFFFFFFF\n81818181818181817F7F7F7F7F7F7F7F\nFF80808080808080007F7F7F7F7F7F7F\nFE0101010101010101FFFFFFFFFFFFFF\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\nFF80B9A2B2A18080007F7F7F7F7F7F7F\nFE0131A9A929010101FFFFFFFFFFFFFF\n00000000000000000000000000000000\nFFFFFFFFFFFFFFFF0000000000000000\n0000000000000000FFFFFFFFFFFFFFFF\nFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\nFF808080808080800000000000000000\n40E0E0E0E0E0E0E0FFFFFFFFFFFFFFFF\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\nFF80B1AAB3A28080007F7F7F7F7F7F7F\nFE0119A1A999010101FFFFFFFFFFFFFF\n3F7FF0FFF0FF403F00000F000F807F3F\nF8FC1EFE1EFE04F80000E000E002FCF8\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n11397D11111101FFFFFFFFFFFFFFFFFF\n1111117D391101FFFFFFFFFFFFFFFFFF\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n3179CDFDCDCD01FFFFFFFFFFFFFFFFFF\n012161FD612101FFFFFFFFFFFFFFFFFF\n011119FD191101FFFFFFFFFFFFFFFFFF\nFF80808080818387007F7F7F7F7F7F7F\nFE0101010181C1E101FFFFFFFFFFFFFF\nFF80808080808080007F7F7F7F7F7F7F\nFF0179FDFDFD490100FFFFB7FFFFFFFF\nFF0155555555550100FFBFFFFBEFFFFF\nFF017D7D7D757D0100FFFFD7FFDFFFFF\nFF809AA2A29B8080007F7F7F7F7F7F7F\nFE01312931A9010101FFFFFFFFFFFFFF\nFF809AA2A2998080007F7F7F7F7F7F7F\nFE01B9919191010101FFFFFFFFFFFFFF\nFF8099A2A2998080007F7F7F7F7F7F7F\nFE0131A9B121010101FFFFFFFFFFFFFF\nFF80B1AAB3A28080007F7F7F7F7F7F7F\nFE0119B189B1010101FFFFFFFFFFFFFF\nFF80B4A4B4A68080007F7F7F7F7F7F7F\nFE01B1A9B1A1010101FFFFFFFFFFFFFF\n8F9F80808080807F7F7F7F7F7F7F7FFF\nF1F90101010101FFFFFFFFFFFFFFFFFF\n808080808080C0FF7F7F7F7F7F7FBFFF\n01010101010103FFFFFFFFFFFFFFFFFF\nFF80ACA9ADAC8080007F7F7F7F7F7F7F\nFE012DA9AD2D010101FFFFFFFFFFFFFF\n808080808080807F7F7F7F7F7F7F7FFF\n11397DF9F16101FFFFEFC7EFFFFFFFFF\n0109D539D50901FFFFFFFFFFFFFFFFFF\nF1F1FDFD3D3D01FFFF9FBFE7E7FFFFFF\n71A9BDBDFD3D01FFFFDFFFE7E7FFFFFF\n0101296D290101FFFFFFFFFFFFFFFFFF\n01113901391101FFFFFFFFFFFFFFFFFF\n1D195545453901FFFFFFFFFFFFFFFFFF\nFF80B6A596B48080007F7F7F7F7F7F7F\nFE0159555555010101FFFFFFFFFFFFFF\nFF80808080809F8F007F7F7F7F7F7F7F\nFE0101010101F9F101FFFFFFFFFFFFFF\n808080C0FFFFFFFF7F7F7FBFFFFFFFFF\n01010103FFFFFFFFFFFFFFFFFFFFFFFF\nFDFDFDFDFDFD01FFE3E3E31F1F1FFFFF\nFDCDCDFDFD7D01FFFFB7B787B7FFFFFF\nFF80BAA2B2A28080007F7F7F7F7F7F7F\nFF019D9991DD010100FFFFFFFFFFFFFF\nFF80A1A2A2B98080007F7F7F7F7F7F7F\nFE0111A9B929010101FFFFFFFFFFFFFF\nFF80B3ABAAAB8080007F7F7F7F7F7F7F\nFE01A92939B9010101FFFFFFFFFFFFFF\n1D3D7D7D7D7D01FFFFE7C7C7C7FFFFFF\nFDF5F5F585FD01FFFF9F9FFFFFFFFFFF\nFF809BB38AB38080007F7F7F7F7F7F7F\nFE01A12121B9010101FFFFFFFFFFFFFF\n878381808080807F7F7F7F7F7F7F7FFF\nE1C18101010101FFFFFFFFFFFFFFFFFF\nFF80B1AAB3A28080007F7F7F7F7F7F7F\nFE0119A1A999010101FFFFFFFFFFFFFF\n808898BE9888807F7F7F7F7F7F7F7FFF\n80888CBE8C88807F7F7F7F7F7F7F7FFF\nFF80B1B2ABB28080007F7F7F7F7F7F7F\nFE0119A1A199010101FFFFFFFFFFFFFF\nFF8099B28BB28080007F7F7F7F7F7F7F\nFE0129A9B991010101FFFFFFFFFFFFFF\nFF809AB28AB28080007F7F7F7F7F7F7F\nFE01DD5991DD010101FFFFFFFFFFFFFF\n2161FDFD612101FFFFFFFFFFFFFFFFFF\nFD9D9DFDFDFD01FFFFF7F7F787FFFFFF\nFF80B9BAAAA98080007F7F7F7F7F7F7F\nFE0129A9B911010101FFFFFFFFFFFFFF\n0000000000000000FFFFFFFFFFFFFFFF\n0018181818001800FFE7E7E7E7FFE7FF\n006C6C2400000000FF9393DBFFFFFFFF\n00247E24247E2400FFDB81DBDB81DBFF\n00083E380E3E0800FFF7C1C7F1C1F7FF\n0062640810264600FF9D9BF7EFD9B9FF\n001C34386E643A00FFE3CBC7919BC5FF\n0018183000000000FFE7E7CFFFFFFFFF\n000C183030180C00FFF3E7CFCFE7F3FF\n0030180C0C183000FFCFE7F3F3E7CFFF\n000024187E182400FFFFDBE781E7DBFF\n000018187E181800FFFFE7E781E7E7FF\n0000000018183000FFFFFFFFE7E7CFFF\n000000007E000000FFFFFFFF81FFFFFF\n0000000000181800FFFFFFFFFFE7E7FF\n00060C1830604000FFF9F3E7CF9FBFFF\n003C666E76663C00FFC399918999C3FF\n0018381818187E00FFE7C7E7E7E781FF\n003C660C18307E00FFC399F3E7CF81FF\n003C660C06663C00FFC399F3F999C3FF\n0066667E06060600FF999981F9F9F9FF\n007E607C06067C00FF819F83F9F983FF\n001C307C66663C00FFE3CF839999C3FF\n007E060C18303000FF81F9F3E7CFCFFF\n003C663C66663C00FFC399C39999C3FF\n003C663E06663C00FFC399C1F999C3FF\n0000001800180000FFFFFFE7FFE7FFFF\n0000001800183000FFFFFFE7FFE7CFFF\n00000C1830180C00FFFFF3E7CFE7F3FF\n0000007E007E0000FFFFFF81FF81FFFF\n000030180C183000FFFFCFE7F3E7CFFF\n003C660C18001800FFC399F3E7FFE7FF\n003C666E6E603C00FFC39991919FC3FF\n00183C667E666600FFE7C399819999FF\n007C667C66667C00FF839983999983FF\n003C666060663C00FFC3999F9F99C3FF\n00786C66666C7800FF879399999387FF\n007E607860607E00FF819F879F9F81FF\n007E607860606000FF819F879F9F9FFF\n003C606E66663C00FFC39F919999C3FF\n0066667E66666600FF999981999999FF\n003C181818183C00FFC3E7E7E7E7C3FF\n001E060606663C00FFE1F9F9F999C3FF\n00666C78786C6600FF999387879399FF\n0060606060607E00FF9F9F9F9F9F81FF\n0042667E7E666600FFBD9981819999FF\n0066767E6E666600FF998981919999FF\n003C666666663C00FFC399999999C3FF\n007C667C60606000FF8399839F9F9FFF\n003C66666A6C3E00FFC399999593C1FF\n007C667C786C6600FF839983879399FF\n003E603C06067C00FFC19FC3F9F983FF\n007E181818181800FF81E7E7E7E7E7FF\n0066666666663C00FF9999999999C3FF\n00666666663C1800FF99999999C3E7FF\n0066667E7E664200FF9999818199BDFF\n00663C183C666600FF99C3E7C39999FF\n0066663C18181800FF9999C3E7E7E7FF\n007E0C1830607E00FF81F3E7CF9F81FF\n003C303030303C00FFC3CFCFCFCFC3FF\n006030180C060200FF9FCFE7F3F9FDFF\n003C0C0C0C0C3C00FFC3F3F3F3F3C3FF\n00183C6600000000FFE7C399FFFFFFFF\n0000000000007E00FFFFFFFFFFFF81FF\nFF017D6545457D0100FFFFDFFFFFFFFF\nFF79B5FDE5DD857900FFCFFFBFBFFFFF\nFF2111EBC7AF110900DFEFF5F9F1FFFF\nFF016D4501456D0100FFFFFFFFFFFFFF\nFF3179793131FDFD00FFFFFFFFFFFF03\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\nFF82878E9CB8B080007F7D7B777F7F7F\nFF80B6BE9CBEB680007F7F6363637F7F\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\nFF0000000000000000FFFFFFFFFFFFFF\nFF80808080808080007F7F7F7F7F7F7F\nFF0179FDFDFD490100FFFFB7FFFFFFFF\nFF0155555555550100FFBFFFFBEFFFFF\nFF017D7D7D757D0100FFFFD7FFDFFFFF\nFF395555D5FD7D3900FFEFEFAF87C7FF\nFF80BAA2B2A28080007F7F7F7F7F7F7F\nFF019D9991DD010100FFFFFFFFFFFFFF\nFF80BAA2B2A38080007F7F7F7F7F7F7F\nFF0159555951010100FFFFFFFFFFFFFF\nFF80B3AAB3A28080007F7F7F7F7F7F7F\nFF0121A121A1010100FFFFFFFFFFFFFF\n01011925251901FFFFFFFFFFFFFFFFFF\n01011909091D01FFFFFFFFFFFFFFFFFF\nFFC1A2948894A2C1007F7F7F7F7F7F7F\nFF8345291129458300FFFFFFFFFFFFFF\n0000000000000000FFFFFFFFFFFFFFFF\n808080808080C0FF7F7F7F7F7F7FBFFF\n01010101010103FFFFFFFFFFFFFFFFFF\n808080C0FFFFFFFF7F7F7FBFFFFFFFFF\n01010103FFFFFFFFFFFFFFFFFFFFFFFF\n0101017D391103FFFFFFFFFFFFFFFFFF\n808080808080807F7F7F7F7F7F7F7FFF\nFDCDCDFDFD7D01FFFFB7B787B7FFFFFF\n01010101010101FFFFFFFFFFFFFFFFFF\n0101296D290101FFFFFFFFFFFFFFFFFF\n01113901391101FFFFFFFFFFFFFFFFFF\n01010D05213101FFFFFFFFFFFFFFFFFF\nFF0111397DF9F16100FFFFEFC7EFFFFF\nFFFFFFE0E0000000FF81BFA0E0000000\nF8F8F8E0E0E0E0E0F888B8A0A0A0A0E0\n3C3CFFFFFFFF3C3C2424E70000E72424\n\n#3:MAIN BG\n00001440010405040504050405040504\n05040504050401040504050401040504\n05040504050405040504010402041000\n10001000100010001000100010000704\n1000100007047E047F04540455044604\n47040404020410001000100010001000\n10001000100007041000100007045604\n2E045604640456045704040402041000\n10001000100010001000100010000704\n1100110007047E047F044E044F044804\n49040404020410001000100010001000\n10001000100007041100110007045604\n2F0456045B0456045804040402041000\n10001000100010001000100010000704\n1200120007047E047F044E044F044A04\n4B040404020410001000100010001000\n10001000100007041200120007045604\n3E0456045C0456045904040402041000\n10001000100010001000100010000704\n1300130007047E047F045E045F044C04\n4D040404020410001000100010001000\n10001000100007041300130007045604\n3F0456045D0456045A04040405040604\n06040604060406040604060406040504\n06040604050406040604060406040604\n060405046E046F040000000000000000\n00000000000000000000000000000000\n00000004000400044004410456046D04\n00000000000000000000000000000000\n00000000000000000000000400040004\n500451046E046F040000000000000000\n00000000000000000000000000000000\n00000004000400046004610456047D04\n00000000000000000000000000000000\n00000000000000000000000400040004\n70047104660467040304030403040304\n03040304030403040304030403040304\n42044304420444044204450456046504\n01040104010401040104010401040104\n01040104010401045204530462046304\n62046304010405040504050405040504\n05040504010401040504050401040504\n05040504050405040504010402041100\n12001300010411011201130104040204\n10001000070401041504010415040104\n15040404020401040104010401040104\n01040104040402041000100007040104\n15040104150401041504040402041102\n12021302010411031203130304040204\n11001100070401041504010415040104\n15040404020401040104010401040104\n01040104040402041100110007040104\n15040104150401041504040402040104\n01040104010401040104010404040204\n12001200070403040304030403040304\n03040404020401040104010401040104\n01040104040402041200120007040104\n01040104010401040104040402044004\n41040104010401046004610404040204\n13001300070401040104010401040104\n01040404020450045104010401040104\n70047104040402041300130007040104\n01040104010401040104040405040604\n06040604060406040604060405040504\n06040604050406040604060406040604\n060405046E046F040000000400040004\n00040000000000000000000000000000\n00000000000000044004410456046D04\n00000000000000000000000000000000\n00000000000000000000000000000004\n500451046E046F040000000000000000\n00000000000000000000000000000000\n00000000000000046004610456047D04\n00000000000000000000000000000000\n00000000000000000000000000000004\n70047104660467040304030403040304\n03040304030403040304030403040304\n42044304420444044204450456046504\n01040104010401040104010401040104\n01040104010401046204630452045304\n62046304000400040004000400040004\n00040004000400040004000400040004\n00040004070476047704040400040004\n00040004000400040004000400040004\n00040004000400040004000407045604\n7C040404000000000000000000000000\n00000000000000000000000400040004\n00040000040406040604010400000000\n00000000000000000000000000000000\n00000000000000000000000407041004\n10040404000000000000000000000000\n00000000000000000000000000000000\n00000004070410041004040400000000\n00000000000000000000000000000000\n00000000000000000000000407041104\n11040404000000000000000000000000\n00000000000000000000000000000000\n00000004070411041104040400000000\n00000000000000000000000000000000\n00000000000000000000000407041204\n12040404000000000000000000000000\n00000000000000000000000000000000\n00000004070412041204040400000000\n00000000000000000000000000000000\n00000000000000000000000407041304\n13040404000000000000000000000000\n00000000000000000000000000000000\n00000004070413041304040400000000\n00000000000000000000000000000000\n00000000000000000000000056040604\n06040504000000000000000000000000\n00000000000000000000000000000000\n00000000010401040104010400000000\n00000000000000000000000000000000\n00000000000000000000000001040104\n01040104000000000000000000000000\n00000000000000000000000000000000\n00000000010401040104010400000000\n00000000000000000000000000000000\n00000000000000000000000001040104\n01040104000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000\n\n#4:DISK BG\n00001420010501050105010501050105\n01050105010501050105010501050105\n01050105010501050105010506040604\n06040604060406040604060406040604\n06040604060406040604060406040604\n06040604010401040104010401040104\n01040104010401040104010401040104\n01040104010401044004410401040104\n01040104010401040104010401040104\n01040104010401040104010401040104\n50045104010401040104010401040104\n01040104010401040104010401040104\n01040104010401040404010401040104\n01040104010401040104010401040104\n01040104010401040104010401040104\n04040104010401040104010401040104\n01040104010401040104010401040104\n01040104010401040404010401040104\n01040104010401040104010401040104\n01040104010401040104010401040104\n04040104010401040104010401040104\n01040104010401040104010401040104\n01040104010401040404010401040104\n01040104010401040104010401040104\n01040104010401040104010401040104\n04040104010401040104010401040104\n01040104010401040104010401040104\n01040104010401040404010401040104\n01040104010401040104010401040104\n01040104010401040104010401040104\n60046104010401040104010401040104\n01040104010401040104010401040104\n01040104010401047004710406040604\n06040604060406040604060406040604\n06040604060406040604060406040604\n06040604760477040304030403040304\n0304030403040304720473040E040F04\n6A046B04680469047804790456047C04\n01040104010401040104010401040104\n7404650456043D0456046C0474046504\n75046504760477040304030403040304\n03040304030403040304030403040304\n6A046B04680469047804790456047C04\n01040104010401040104010401040104\n010401040104010456046C0474046504\n75046504760477040304030403040304\n0304030403040304030403047A047B04\n6A046B04680469047804790456047C04\n01040104010401040104010401040104\n0104010456046D0456046C0474046504\n75046504000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000\n\n#5:SIZE BG\n00001410010701070107010701070107\n01070107010701070107010701070107\n01070107010701070107010703070307\n03070307030703070307030703070307\n03070307030703070307030703070307\n03070307010701070107010701070107\n01070107010701070107010701070107\n01070107010701070107010701070507\n05070507050705070107050705070507\n05070507010701070507050705070507\n05070107020701070107010701070107\n07070107010701070107010704070207\n01070107010701070107040702070107\n01070107010701070707010701070107\n01070107040702070107010701070107\n01070407020701070107010701070107\n07070107010701070107010704070207\n01070107010701070107040702070107\n01070107010701070707010701070107\n01070107040702070107010701070107\n01070407020701070107010701070107\n07070107010701070107010704070207\n01070107010701070107040702070107\n01070107010701070707010701070107\n01070107040702070107010701070107\n01070407010703070307030703070307\n01070307030703070307030701070107\n03070307030703070307010701070107\n01070107010701070107010701070107\n01070107010701070107010701070107\n01070107010701040104010401040104\n01040104010401040104010401040104\n01040104010401040104010705040504\n05040504050405040504050405040504\n05040504050405040504050405040504\n05040504080403040304030403040304\n03040304030409040804030403040304\n03040304030403040304090404040104\n01040104010401040104010401040204\n04040104010401040104010401040104\n01040204\n\n#10:DEFAULT PALETTES\n053F2F0000383400003C0C00003F3C00\n003F2A15003F2A15003F2A15003F2A15\n\n#11: FONTS\n00000000000000000000000000000000\n00181818180018000000000000000000\n006C6C24000000000000000000000000\n00247E24247E24000000000000000000\n00083E380E3E08000000000000000000\n00626408102646000000000000000000\n001C34386E643A000000000000000000\n00181830000000000000000000000000\n000C183030180C000000000000000000\n0030180C0C1830000000000000000000\n000024187E1824000000000000000000\n000018187E1818000000000000000000\n00000000181830000000000000000000\n000000007E0000000000000000000000\n00000000001818000000000000000000\n00060C18306040000000000000000000\n003C666E76663C000000000000000000\n0018381818187E000000000000000000\n003C660C18307E000000000000000000\n003C660C06663C000000000000000000\n0066667E060606000000000000000000\n007E607C06067C000000000000000000\n001C307C66663C000000000000000000\n007E060C183030000000000000000000\n003C663C66663C000000000000000000\n003C663E06663C000000000000000000\n00000018001800000000000000000000\n00000018001830000000000000000000\n00000C1830180C000000000000000000\n0000007E007E00000000000000000000\n000030180C1830000000000000000000\n003C660C180018000000000000000000\n003C666E6E603C000000000000000000\n00183C667E6666000000000000000000\n007C667C66667C000000000000000000\n003C666060663C000000000000000000\n00786C66666C78000000000000000000\n007E607860607E000000000000000000\n007E6078606060000000000000000000\n003C606E66663C000000000000000000\n0066667E666666000000000000000000\n003C181818183C000000000000000000\n001E060606663C000000000000000000\n00666C78786C66000000000000000000\n0060606060607E000000000000000000\n0042667E7E6666000000000000000000\n0066767E6E6666000000000000000000\n003C666666663C000000000000000000\n007C667C606060000000000000000000\n003C66666A6C3E000000000000000000\n007C667C786C66000000000000000000\n003E603C06067C000000000000000000\n007E1818181818000000000000000000\n0066666666663C000000000000000000\n00666666663C18000000000000000000\n0066667E7E6642000000000000000000\n00663C183C6666000000000000000000\n0066663C181818000000000000000000\n007E0C1830607E000000000000000000\n003C303030303C000000000000000000\n006030180C0602000000000000000000\n003C0C0C0C0C3C000000000000000000\n00183C66000000000000000000000000\n0000000000007E000000000000000000\n0000000000000000FFFFFFFFFFFFFFFF\n0018181818001800FFE7E7E7E7FFE7FF\n006C6C2400000000FF9393DBFFFFFFFF\n00247E24247E2400FFDB81DBDB81DBFF\n00083E380E3E0800FFF7C1C7F1C1F7FF\n0062640810264600FF9D9BF7EFD9B9FF\n001C34386E643A00FFE3CBC7919BC5FF\n0018183000000000FFE7E7CFFFFFFFFF\n000C183030180C00FFF3E7CFCFE7F3FF\n0030180C0C183000FFCFE7F3F3E7CFFF\n000024187E182400FFFFDBE781E7DBFF\n000018187E181800FFFFE7E781E7E7FF\n0000000018183000FFFFFFFFE7E7CFFF\n000000007E000000FFFFFFFF81FFFFFF\n0000000000181800FFFFFFFFFFE7E7FF\n00060C1830604000FFF9F3E7CF9FBFFF\n003C666E76663C00FFC399918999C3FF\n0018381818187E00FFE7C7E7E7E781FF\n003C660C18307E00FFC399F3E7CF81FF\n003C660C06663C00FFC399F3F999C3FF\n0066667E06060600FF999981F9F9F9FF\n007E607C06067C00FF819F83F9F983FF\n001C307C66663C00FFE3CF839999C3FF\n007E060C18303000FF81F9F3E7CFCFFF\n003C663C66663C00FFC399C39999C3FF\n003C663E06663C00FFC399C1F999C3FF\n0000001800180000FFFFFFE7FFE7FFFF\n0000001800183000FFFFFFE7FFE7CFFF\n00000C1830180C00FFFFF3E7CFE7F3FF\n0000007E007E0000FFFFFF81FF81FFFF\n000030180C183000FFFFCFE7F3E7CFFF\n003C660C18001800FFC399F3E7FFE7FF\n003C666E6E603C00FFC39991919FC3FF\n00183C667E666600FFE7C399819999FF\n007C667C66667C00FF839983999983FF\n003C666060663C00FFC3999F9F99C3FF\n00786C66666C7800FF879399999387FF\n007E607860607E00FF819F879F9F81FF\n007E607860606000FF819F879F9F9FFF\n003C606E66663C00FFC39F919999C3FF\n0066667E66666600FF999981999999FF\n003C181818183C00FFC3E7E7E7E7C3FF\n001E060606663C00FFE1F9F9F999C3FF\n00666C78786C6600FF999387879399FF\n0060606060607E00FF9F9F9F9F9F81FF\n0042667E7E666600FFBD9981819999FF\n0066767E6E666600FF998981919999FF\n003C666666663C00FFC399999999C3FF\n007C667C60606000FF8399839F9F9FFF\n003C66666A6C3E00FFC399999593C1FF\n007C667C786C6600FF839983879399FF\n003E603C06067C00FFC19FC3F9F983FF\n007E181818181800FF81E7E7E7E7E7FF\n0066666666663C00FF9999999999C3FF\n00666666663C1800FF99999999C3E7FF\n0066667E7E664200FF9999818199BDFF\n00663C183C666600FF99C3E7C39999FF\n0066663C18181800FF9999C3E7E7E7FF\n007E0C1830607E00FF81F3E7CF9F81FF\n003C303030303C00FFC3CFCFCFCFC3FF\n006030180C060200FF9FCFE7F3F9FDFF\n003C0C0C0C0C3C00FFC3F3F3F3F3C3FF\n00183C6600000000FFE7C399FFFFFFFF\n0000000000007E00FFFFFFFFFFFF81FF\n00000000000000000000000000000000\n183C3C3C3C183C181824242424182418\n6CFEFE7E240000006C92925A24000000\n247EFF7E7EFF7E24245A815A5A815A24\n083E7F7E3F7F3E080836414631413608\n62F7FE7C3E7FEF4662959A742E59A946\n1C3E7E7EFFFE7F3A1C224A46919A453A\n183C3C78300000001824244830000000\n0C1E3C78783C1E0C0C1224484824120C\n30783C1E1E3C78303048241212244830\n00247E7EFF7E7E2400245A6681665A24\n00183C7EFF7E3C180018246681662418\n000000183C3C78300000001824244830\n0000007EFF7E00000000007E817E0000\n00000000183C3C180000000018242418\n060F1E3C78F0E040060912244890A040\n3C7EFFFFFFFF7E3C3C4299918999423C\n183C7C3C3C7EFF7E182444242466817E\n3C7EFF7E3C7EFF7E3C429972244E817E\n3C7EFF7E6FFF7E3C3C4299726999423C\n66FFFFFF7F0F0F066699998179090906\n7EFFFEFE7F7FFE7C7E819E827979827C\n1C3E7CFEFFFF7E3C1C224C829999423C\n7EFF7F1E3C7878307E81791224484830\n3C7EFF7EFFFF7E3C3C4299429999423C\n3C7EFF7F7FFF7E3C3C4299417999423C\n0000183C183C18000000182418241800\n0000183C183C78300000182418244830\n000C1E3C783C1E0C000C12244824120C\n00007EFF7EFF7E0000007E817E817E00\n0030783C1E3C78300030482412244830\n3C7EFF7E3C183C183C42997224182418\n3C7EFFFFFFFE7E3C3C429991919E423C\n183C7EFFFFFFFF661824429981999966\n7CFEFFFEFFFFFE7C7C8299829999827C\n3C7EFFF6F6FF7E3C3C4299969699423C\n78FCFEFFFFFEFC787884929999928478\n7EFFFEFCF8FEFF7E7E819E84989E817E\n7EFFFEFCF8F0F0607E819E8498909060\n3C7EFEFFFFFF7E3C3C429E919999423C\n66FFFFFFFFFFFF666699998199999966\n3C7E3C3C3C3C7E3C3C4224242424423C\n1E3F1F0F6FFF7E3C1E2119096999423C\n66FFFEFCFCFEFF666699928484929966\n60F0F0F0F0FEFF7E60909090909E817E\n42E7FFFFFFFFFF6642A5998181999966\n66FFFFFFFFFFFF666699898191999966\n3C7EFFFFFFFF7E3C3C4299999999423C\n7CFEFFFEFCF0F0607C8299829C909060\n3C7EFFFFFFFE7F3E3C4299999592413E\n7CFEFFFEFCFEFF667C82998284929966\n3E7FFE7E3F7FFE7C3E419E423979827C\n7EFF7E3C3C3C3C187E81662424242418\n66FFFFFFFFFF7E3C669999999999423C\n66FFFFFFFF7E3C186699999999422418\n66FFFFFFFFFFE742669999818199A542\n66FF7E3C7EFFFF666699422442999966\n66FFFF7E3C3C3C186699994224242418\n7EFF7E3C78FEFF7E7E817224489E817E\n3C7E7C78787C7E3C3C424C48484C423C\n60F0783C1E0F07026090482412090502\n3C7E3E1E1E3E7E3C3C4232121232423C\n183C7EFF660000001824429966000000\n00000000007EFF7E00000000007E817E\n00000000000000000000000000000000\n00181C1C1C0C180C00000404040C000C\n006C7E36120000000000121212000000\n00247E3F367E3F120000001B12001B12\n00083E3F1E3F1F040000000710011704\n0062753A142E57230000113204081123\n001C3E3A7E773A1D00000A021013001D\n00181C3C180000000000040C18000000\n000C1E3C38180C060000060C08000006\n0030180C0E1E3C180000000002060C18\n0000241A7E3F2C120000000200270812\n0000181C7E3F1C0C000000040027040C\n00000000181C3C180000000000040C18\n000000007E3F000000000000003F0000\n0000000000181C0C000000000000040C\n00060F1E3C787020000003060C183020\n003C7E7F777F3F1E000018110119031E\n00183C1C1C1C7E3F000004040404003F\n003C7E3F1E3C7E3F00001833060C003F\n003C7E3F06673F1E000018330001031E\n0066777F3F0707030000110139010103\n007E7F7C3E077F3E00001F003801033E\n001C3E7C7E773F1E00000E001811031E\n007E3F0F1E3C381800003903060C0818\n003C7E3F7E773F1E000018031811031E\n003C7E3F1F673F1E000018011901031E\n000000180C180C00000000000C000C00\n000000180C183C18000000000C000C18\n00000C1E3C180C06000000060C000006\n0000007E3F7E3F00000000003F003F00\n000030180C1E3C180000000000060C18\n003C7E3F1E0C180C00001833060C000C\n003C7E7F7F773C1E000018111117001E\n00183C7E7F7F77330000001801191133\n007C7E7F7E777F3E000018031811033E\n003C7E7370763F1E000018131010031E\n00787C76777F7E3C000010101113063C\n007E7F787C707E3F00001F001C10003F\n007E7F787C70703000001F001C101030\n003C7E7E77773F1E00001E101111031E\n0066777F7F7777330000110119111133\n003C1E1C1C1C3C1E000006040404001E\n001E0F0707673F1E000009010101031E\n00667F7E7C7C76330000130604101033\n0060707070707E3F000010101010003F\n0042677F7F7F77330000010101191133\n0066777F7F7777330000010111111133\n003C7E7777773F1E000018111111031E\n007C7E7F7E707030000018031E101030\n003C7E777B7D3E1F000018111111001F\n007C7E7F7E7C76330000180306101033\n003E7F3C1E077F3E00001F001801033E\n007E3F1C1C1C1C0C000027040404040C\n0066777777773F1E000011111111031E\n00667777773F1E0C000011111103060C\n0066777F7F7F73210000110101193121\n00663F1E3C7E77330000030600181133\n0066773F1E1C1C0C000011030604040C\n007E3F1E3C787E3F000033060C18003F\n003C3E3838383C1E00000E080808001E\n006030180C0603010000000000000101\n003C1E0E0E0E3E1E000012020202021E\n00183C7E330000000000001833000000\n0000000000007E3F000000000000003F\n\n"
  },
  {
    "path": "programs/LowRes Adventure 1.1.nx",
    "content": "'TITLE:   LOWRES ADVENTURE\n'AUTHOR:  TIMO KLOSS\n\nSOUND 0,0,0,0\nSOUND 1,3,0,0\n\nSTARTADV:\nHEALTH=10\nNAME$=\"\"\nKEY=0\n\nCLS\nPRINT\nPRINT\nPRINT\nPRINT \"  -======**======-\"\nPRINT \"  LOWRES ADVENTURE\"\nPRINT \"  -======**======-\"\nGOSUB TITLESONG\nCLS\nGOSUB STATUS\nPRINT \"YOU ARRIVE AT THE\"\nPRINT \"KING'S CASTLE.\"\nPRINT \"KING: FINALLY YOU\"\nPRINT \"ARE HERE TO RESCUE\"\nPRINT \"OUR COUNTRY! THE\"\nPRINT \"GHOSTS FROM THE\"\nPRINT \"SHADOW LAND CAME AND\";\nPRINT \"STOLE MY CROWN WHEN\"\nPRINT \"I WAS SLEEPING!\"\nPRINT \"THEY SAY YOU ARE THE\";\nPRINT \"ONLY ONE WHO CAN\"\nPRINT \"HELP US. WHAT IS\"\nPRINT \"YOUR NAME?\"\nREPEAT\n  INPUT \">\";NAME$\nUNTIL LEN(NAME$)>0\n\nPRINT\nPRINT \"KING: SO, \"+NAME$+\",\"\nPRINT \"I HAVE CHOSEN YOU TO\";\nPRINT \"BRING BACK MY CROWN.\";\nPRINT \"BUT BE CAREFUL, THE\"\nPRINT \"SHADOW LAND IS\"\nPRINT \"DANGEROUS!\"\nPRINT \"ARE YOU READY?\"\nPRINT \"1. YES!\"\nPRINT \"2. NO, THANKS\"\nDO\n  INPUT \">\";A\n  IF A=1 THEN GOTO CITY\n  IF A=2 THEN GOTO CASTLENO\nLOOP\n\nCASTLENO:\nPRINT\nPRINT \"KING: WELL OK, I\"\nPRINT \"GUESS IT'S FINE, I\"\nPRINT \"DON'T REALLY NEED MY\";\nPRINT \"CROWN.\"\nGOTO GAMEOVER\n\nCITY:\nPRINT\nPRINT \"YOU ARE IN THE\"\nPRINT \"KING'S CITY. FROM\"\nPRINT \"HERE YOU HAVE\"\nPRINT \"SEVERAL WAYS.\"\nPRINT \"1. GO TO SHADOW LAND\";\nPRINT \"2. GO TO RIVER\"\nDO\n  INPUT \">\";A\n  IF A=1 THEN GOTO SHADOW\n  IF A=2 THEN GOTO RIVER\nLOOP\n\nSHADOW:\nPRINT\nPRINT \"A DARK AND SCARY\"\nPRINT \"PATH LEADS YOU TO A\"\nPRINT \"GIANT DOOR OF A\"\nPRINT \"BLACK MOUNTAIN.\"\nPRINT \"1. OPEN DOOR\"\nPRINT \"2. RETURN TO CITY\"\nDO\n  INPUT \">\";A\n  IF A=1 THEN\n    IF KEY THEN GOTO SHADOWOPEN ELSE GOTO SHADOWLOCKED\n  END IF\n  IF A=2 THEN GOTO CITY\nLOOP\n\nSHADOWLOCKED:\nPRINT\nPRINT \"THE DOOR IS LOCKED,\"\nPRINT \"YOU NEED A KEY.\"\nPRINT \"YOU DECIDE TO RETURN\";\nPRINT \"TO THE CITY.\"\nINPUT \"> PRESS ENTER\";A\nGOTO CITY\n\nSHADOWOPEN:\nPRINT\nPRINT \"YOU OPEN THE DOOR\"\nPRINT \"WITH YOUR KEY AND\"\nPRINT \"ENTER A GIANT CAVE.\"\nPRINT \"AT THE END OF THE\"\nPRINT \"CAVE YOU SEE A GHOST\";\nPRINT \"WITH THE CROWN OF\"\nPRINT \"YOUR KING ON HIS\"\nPRINT \"HEAD.\"\nPRINT \"1. ATTACK GHOST\"\nPRINT \"2. TALK WITH GHOST\"\nPRINT \"3. RETURN TO CITY\"\nDO\n  INPUT \">\";A\n  IF A=1 THEN GOTO GHOSTATTACK\n  IF A=2 THEN GOTO GHOSTTALK\n  IF A=3 THEN GOTO CITY\nLOOP\n\nGHOSTATTACK:\nPRINT\nPRINT \"THE FINAL FIGHT WITH\";\nPRINT \"THE GHOST OF THE\"\nPRINT \"SHADOW LAND BEGINS.\"\nENEMYHEALTH=5\nGOSUB FIGHT\nPRINT\nIF ENEMYHEALTH=0 THEN\n  GOTO GHOSTDEFEATED\nELSE\n  PRINT \"YOU RUN AWAY FROM\"\n  PRINT \"THE MOUNTAIN.\"\n  INPUT \"> PRESS ENTER\";A\n  GOTO CITY\nEND IF\n\nGHOSTDEFEATED:\nPRINT\nPRINT \"GHOST: OKAY, OKAY,\"\nPRINT \"STOP PLEASE!\"\nPRINT \"WHAT DO YOU WANT?\"\nPRINT \"YOU: THE KING'S\"\nPRINT \"CROWN!\"\nPRINT \"GHOST: AH, WHY\"\nPRINT \"DIDN'T YOU TELL IT\"\nPRINT \"BEFORE?! I DON'T\"\nPRINT \"LIKE IT ANYWAY.\"\nPRINT \"HERE, TAKE IT!\"\nINPUT \"> PRESS ENTER\";A\nPRINT\nPRINT \"YOU: OH, THANKS!\"\nPRINT \"SORRY FOR THAT\"\nPRINT \"LITTLE FIGHT. I\"\nPRINT \"DIDN'T WANT TO\"\nPRINT \"OFFEND YOU.\"\nPRINT \"GHOST: DON'T WORRY.\"\nPRINT \"SEE YOU SOON.\"\nINPUT \"> PRESS ENTER\";A\nGOTO GAMEEND\n\nGHOSTTALK:\nPRINT\nPRINT \"GHOST: HELLO \"+NAME$+\",\"\nPRINT \"HOW ARE YOU? I SEE\"\nPRINT \"YOU FOUND THE KEY TO\";\nPRINT \"MY MOUNTAIN, NOT\"\nPRINT \"BAD. WHAT DID YOU\"\nPRINT \"COME FOR?\"\nPRINT \"1. TO DEFEAT YOU!\"\nPRINT \"2. TO ASK FOR THE\"\nPRINT \"   CROWN\"\nPRINT \"3. NOTHING, BYE!\"\nDO\n  INPUT \">\";A\n  IF A=1 THEN GOTO GHOSTATTACK\n  IF A=2 THEN GOTO GHOSTCROWN\n  IF A=3 THEN GOTO CITY\nLOOP\n\nGHOSTCROWN:\nPRINT\nPRINT \"GHOST: YEAH, YOU\"\nPRINT \"KNOW, I FOUND IT\"\nPRINT \"SOMEWHERE ON THE\"\nPRINT \"STREET. IT ACTUALLY\"\nPRINT \"DOESN'T FIT ME VERY\"\nPRINT \"WELL. YOU CAN HAVE\"\nPRINT \"IT, NO PROBLEM.\"\nPRINT \"YOU: OH COOL,\"\nPRINT \"THANKS!\"\nINPUT \"> PRESS ENTER\";A\nGOTO GAMEEND\n\nRIVER:\nPRINT\nPRINT \"YOU ARE AT THE\"\nPRINT \"RIVER. THERE IS A\"\nPRINT \"BIG WATERFALL.\"\nPRINT \"1. GO FISHING\"\nPRINT \"2. GO BEHIND\"\nPRINT \"   WATERFALL\"\nPRINT \"3. RETURN TO CITY\"\nDO\n  INPUT \">\";A\n  IF A=1 THEN GOTO FISHING\n  IF A=2 THEN GOTO WATERFALL\n  IF A=3 THEN GOTO CITY\nLOOP\n\nFISHING:\nPRINT\nR=RND\nIF R<0.1 THEN\n  PRINT \"YOU FISH AN OLD\"\n  PRINT \"SHOE. JUST IN CASE\"\n  PRINT \"YOU LOOK INSIDE AND\"\n  PRINT \"YOU FIND A SPECIAL\"\n  PRINT \"HEALING POTION!\"\n  HEALTH=HEALTH+4\nELSE IF R<0.6 THEN\n  PRINT \"YOU JUMP INTO THE\"\n  PRINT \"WATER AND GRAB A\"\n  PRINT \"FISH WITH YOUR\"\n  PRINT \"HANDS. IT LOOKS\"\n  PRINT \"TASTY AND YOU EAT\"\n  PRINT \"IT.\"\n  HEALTH=HEALTH+1\nELSE\n  PRINT \"YOU TRY TO CATCH A\"\n  PRINT \"FISH BUT IT BITES\"\n  PRINT \"YOU IN THE HAND AND\"\n  PRINT \"YOU FALL\"\n  PRINT \"UNFAVORABLY.\"\n  HEALTH=HEALTH-2\nEND IF\nGOSUB STATUS\nINPUT \"> PRESS ENTER\";A\nGOTO RIVER\n\nWATERFALL:\nPRINT\nPRINT \"BEHIND THE WATERFALL\";\nPRINT \"YOU FIND THE\"\nPRINT \"ENTRANCE TO A CAVE.\"\nPRINT \"FROM INSIDE YOU CAN\"\nPRINT \"HEAR SOME STRANGE\"\nPRINT \"SOUNDS.\"\nPRINT \"1. ENTER CAVE\"\nPRINT \"2. RETURN TO RIVER\"\nDO\n  INPUT \">\";A\n  IF A=1 THEN GOTO CAVE\n  IF A=2 THEN GOTO RIVER\nLOOP\n\nCAVE:\nPRINT\nPRINT \"A CAVE MONSTER\"\nPRINT \"ATTACKS YOU!\"\nENEMYHEALTH=3\nGOSUB FIGHT\nPRINT\nIF ENEMYHEALTH=0 THEN\n  PRINT \"THE MONSTER FEELS\"\n  PRINT \"QUITE BAD AND\"\n  PRINT \"GOES TO SLEEP. YOU\"\n  PRINT \"FIND A KEY IN THE\"\n  PRINT \"CAVE AND TAKE IT.\"\n  KEY=-1\nELSE\n  PRINT \"YOU RUN AWAY FROM\"\n  PRINT \"THE CAVE.\"\nEND IF\nINPUT \"> PRESS ENTER\";A\nGOTO RIVER\n\nGAMEEND:\nPRINT\nPRINT \"YOU RETURN TO THE\"\nPRINT \"KING'S CASTLE AND\"\nPRINT \"GIVE THE CROWN BACK\"\nPRINT \"TO HIM.\"\nPRINT \"KING: WELL DONE,\"\nPRINT NAME$+\"!\"\nPRINT \"NOW PEACE AND\"\nPRINT \"HARMONY WILL RETURN\"\nPRINT \"TO OUR LAND.\"\nPRINT \"YOU: YEAH, SURE.\"\nPRINT\nPRINT \"YOU GO HOME AND\"\nPRINT \"WATCH TV.\"\nPRINT\nPRINT \"THE END\"\nGOSUB TITLESONG\nGOTO RESTART\n\n\nFIGHT:\nDO\n  PRINT\n  IF RND<0.5 THEN\n    PRINT \"THE ENEMY HITS YOU!\"\n    PLAY 1,32,5\n    HEALTH=HEALTH-1\n  ELSE\n    PRINT \"YOU AVOID THE\"\n    PRINT \"ENEMY'S ATTACK.\"\n    PLAY 0,32,0\n    WAIT 5\n    PLAY 0,39,5\n  END IF\n  GOSUB STATUS\n  PRINT \"> (A)TTACK OR\"\n  PRINT \"  (F)LEE?\"\n  DO\n    I$=INKEY$\n    IF I$=\"A\" THEN GOTO ATTACK\n    IF I$=\"F\" THEN RETURN\n    WAIT VBL\n  LOOP\n  ATTACK:\n  PRINT\n  IF RND<0.6 THEN\n    PRINT \"YOU HIT THE ENEMY!\"\n    PLAY 1,39,5\n    ENEMYHEALTH=ENEMYHEALTH-1\n    IF ENEMYHEALTH=0 THEN RETURN\n  ELSE\n    PRINT \"THE ENEMY AVOIDS\"\n    PRINT \"YOUR ATTACK.\"\n    PLAY 0,37,0\n    WAIT 5\n    PLAY 0,30,5\n  END IF\n  PRINT \"-- ENEMY'S H. \"+STR$(ENEMYHEALTH)+\" --\"\nLOOP\nRETURN\n\n\nSTATUS:\nIF HEALTH<0 THEN HEALTH=0\nPRINT \"-- HEALTH \"+STR$(HEALTH)+\" --\"\nIF HEALTH=0 THEN RETURN GAMEOVER\nRETURN\n\nGAMEOVER:\nPRINT\nPRINT \"GAME OVER\"\nGOSUB GAMEOVERSONG\n\nRESTART:\nPRINT \"> RESTART? (Y/N)\"\nDO\n  I$=INKEY$\n  IF I$=\"Y\" THEN GOTO STARTADV\n  IF I$=\"N\" THEN END\n  WAIT VBL\nLOOP\n\nTITLESONG:\nPLAY 0,37,0\nWAIT 12\nPLAY 0,25\nWAIT 12\nPLAY 0,41\nWAIT 12\nPLAY 0,39\nWAIT 12\nPLAY 0,27\nWAIT 12\nPLAY 0,42\nWAIT 12\nPLAY 0,41\nWAIT 12\nPLAY 0,29\nWAIT 12\nPLAY 0,44\nWAIT 12\nPLAY 0,42\nWAIT 12\nPLAY 0,39\nWAIT 12\nPLAY 0,41\nWAIT 12\nPLAY 0,37\nWAIT 24\nPLAY 0,25\nWAIT 24\nSTOP\nRETURN\n\nGAMEOVERSONG:\nPLAY 0,44,0\nWAIT 12\nPLAY 0,43\nWAIT 12\nPLAY 0,42\nWAIT 12\nPLAY 0,41\nWAIT 12\nPLAY 0,40\nWAIT 24\nSTOP\nRETURN\n"
  },
  {
    "path": "programs/LowRes Galaxy 2 (1.5).nx",
    "content": "'TITLE:   LOWRES GALAXY 2\n'AUTHOR:  TIMO KLOSS\n\nRANDOMIZE TIMER\n\n' SPRITES\n' 0 PLAYER SHIP\n' 1 JET\n' 2-9 PLAYER BULLETS\n' 10-25 ALIENS\n' 26-29 EXPLOSIONS\n' 30-39 ALIEN BULLETS\n\n'POINTS FOR ALIENS\nDIM GLOBAL POINTS(4)\nDATA 0,10,20,400,600\nFOR I=0 TO 4\n  READ POINTS(I)\nNEXT I\n\n'ALIENS\nDIM GLOBAL ALIENS(15,5)\n' 0 TYPE (0=DISABLED)\n' 1 TICK\n' 2 START Y\n' 3 HITS\n' 4 AMPLITUDE\n' 5 SPEED\n\n'ALIEN BULLETS\nDIM GLOBAL ABULLETS(9,3)\n' 0 X\n' 1 Y\n' 2 X VECTOR\n' 3 Y VECTOR\n\n'EXPLOSION TICKS\nDIM GLOBAL EXPLOSIONS(3)\n\nGLOBAL HIGHSCORE\nGLOBAL TICK,BGTICK\nGLOBAL LEVEL,SCORE,LIVES\nGLOBAL PX,PY\nGLOBAL SHIELD,HIDE,PEACE\nGLOBAL SHDELAY,HEAT,BULLET\nGLOBAL EXPLOSION\nGLOBAL ALIEN,ABULLET\nGLOBAL MSGTIMER\n\nFONT 128\n\nCALL DRAWGAMEBG\nON RASTER CALL RASTERFX\n\nGAMEPAD 1\n\n'READ FROM PERSISTENT RAM\nHIGHSCORE=PEEKL($E000)\n\n\nTITLE:\n\nTICK=0\n\nSPRITE OFF\nCALL DRAWTITLE\nSOUND SOURCE ROM(15)\nMUSIC 0\nREPEAT\n  INC BGTICK\n  IF TICK MOD 240=0 THEN\n    CALL SHOWMSG(\"PRESS ANY BUTTON\")\n  ELSE IF TICK MOD 240=120 THEN\n    CALL SHOWMSG(\"HIGHSCORE: \"+STR$(HIGHSCORE))\n  END IF\n  INC TICK\n  WAIT VBL\nUNTIL BUTTON TAP(0)\nSTOP\n\n\nGAME:\n\n'INIT PLAYER SPRITES\nSPRITE 0 PAL 4 SIZE 1\nSPRITE 1 PAL 5\nFOR I=2 TO 9\n  SPRITE I PAL 5\nNEXT I\n\n'INIT VARIABLES\nLIVES=5\nSCORE=0\nLEVEL=0\nPX=32\nPY=48\nSHIELD=120\nHIDE=0\nPEACE=0\nSHDELAY=0\nHEAT=0\nTICK=0\nBULLET=0\nALIEN=0\n\nFOR I=0 TO 15\n  ALIENS(I,0)=0\nNEXT I\nFOR I=0 TO 9\n  ABULLETS(I,0)=-32\nNEXT I\n\nCALL CLEAROVERLAYS\nCALL DRAWHUD\n\nCALL RESETSOUND\nSOUND SOURCE ROM(15)\nMUSIC 8\nSOUND SOURCE ROM(14)\n\n'GAME LOOP\nDO\n  'NEXT LEVEL?\n  IF LIVES>0 AND TICK MOD 1800=0 THEN\n    INC LEVEL\n    CALL SHOWMSG(\"LEVEL \"+STR$(LEVEL))\n    IF LEVEL>=2 THEN\n      CALL ADDSCORE(LEVEL*100)\n      TRACK 0,3\n    END IF\n  END IF\n\n  IF PEACE>0 THEN\n    'DO NOT SPAWN ALIENS\n    DEC PEACE\n  ELSE\n    'SPAWN SMALL ALIEN?\n    M=480/(LEVEL+3)\n    IF TICK MOD M=0 THEN\n      CALL SPAWNALIEN(1+RND(1))\n    END IF\n \n    'SPAWN BIG ALIEN?\n    M=5400/(LEVEL+2)\n    IF TICK MOD M=M\\2 THEN\n      CALL SPAWNALIEN(3+RND(1))\n    END IF\n  END IF\n \n  CALL UPDALIENS\n  CALL UPDBULLETS\n  CALL UPDALIENBULLETS\n\n  IF LIVES>0 THEN\n    CALL UPDPLAYER\n  ELSE\n    'GAME OVER\n    IF MSGTIMER>0 THEN\n      DEC MSGTIMER\n    ELSE\n      IF BUTTON TAP(0) THEN GOTO TITLE\n    END IF\n  END IF\n\n  CALL UPDEXPLOSIONS\n  CALL UPDMSG\n \n  INC TICK\n  INC BGTICK\n\n  WAIT VBL\nLOOP\n\nSUB ADDSCORE(P)\n  ADD SCORE,P\n  CALL DRAWHUD\nEND SUB\n\nSUB UPDPLAYER\n  IF HIDE>0 THEN\n    DEC HIDE\n    SPRITE OFF 0 TO 1\n    EXIT SUB\n  END IF\n\n  'PLAYER CONTROL\n  IF UP(0) AND PY>0 THEN DEC PY\n  IF DOWN(0) AND PY<112 THEN INC PY\n  IF LEFT(0) AND PX>8 THEN DEC PX\n  IF RIGHT(0) AND PX<128 THEN INC PX\n\n  'PLAYER SPRITE\n  IF SHIELD>0 AND SHIELD MOD 4<2 THEN\n    SPRITE OFF 0 TO 1\n  ELSE\n    SPRITE 0,PX,PY,1\n    SPRITE 1,PX-8,PY+5,19+INT((TICK MOD 16)/8)\n  END IF\n\n  'SHOOT?\n  SHDELAY=SHDELAY-1\n  IF BUTTON(0) THEN\n    IF SHDELAY<=0 THEN\n      SPRITE BULLET+2,PX+8,PY+11,3\n      BULLET=(BULLET+1) MOD 8\n      PLAY 2,50+RND*2 SOUND 0\n      HEAT=HEAT+1\n      IF HEAT>=5 THEN\n        SHDELAY=30\n        HEAT=0\n      ELSE\n        SHDELAY=8\n      END IF\n    END IF\n  ELSE\n    HEAT=0\n  END IF\n\n  IF SHIELD>0 THEN\n    DEC SHIELD\n  ELSE\n\n    'PLAYER HIT BY BULLET?\n    IF SPRITE HIT(0,30 TO 39) THEN\n      SPRITE OFF HIT\n      ABULLETS(HIT-30,0)=-32\n      CALL LOSESHIP\n    END IF\n\n    'COLLISION WITH ALIEN?\n    IF SPRITE HIT(0,10 TO 25) THEN\n      CALL LOSESHIP\n    END IF\n\n  END IF\nEND SUB\n\nSUB LOSESHIP\n  DEC LIVES\n  CALL DRAWHUD\n  SPRITE OFF 0 TO 1\n  PLAY 2,30 SOUND 4\n  CALL EXPLODE(PX,PY,0)\n  CALL EXPLODE(PX-8+RND*16,PY-8+RND*16,10)\n  CALL EXPLODE(PX-8+RND*16,PY-8+RND*16,30)\n  IF LIVES=0 THEN\n    CALL DRAWGAMEOVER\n    IF SCORE>HIGHSCORE THEN\n      HIGHSCORE=SCORE\n      CALL SHOWMSG(\"NEW HIGHSCORE!\")\n      'WRITE TO PERSISTENT RAM\n      POKEL $E000,HIGHSCORE\n    ELSE\n      CALL SHOWMSG(\"HIGHSCORE: \"+STR$(HIGHSCORE))\n    END IF\n    SOUND SOURCE ROM(15)\n    MUSIC 32\n    SOUND SOURCE ROM(14)\n    MSGTIMER=100\n  ELSE\n    HIDE=120\n    SHIELD=120\n    PEACE=180\n  END IF\nEND SUB\n\nSUB UPDBULLETS\n  FOR I=2 TO 9\n    IF SPRITE.X(I)>=0 THEN\n      SPRITE I,SPRITE.X(I)+3,,\n      IF SPRITE.X(I)>160 THEN SPRITE OFF I\n    END IF\n  NEXT I\nEND SUB\n\nSUB SPAWNALIEN(TYPE)\n  I=ALIEN\n  IF ALIENS(I,0)>0 THEN EXIT SUB\n  ALIENS(I,0)=TYPE\n  ALIENS(I,1)=0\n  ALIENS(I,2)=16+RND*80\n  IF TYPE=1 THEN\n    ALIENS(I,3)=1\n    ALIENS(I,4)=RND*12\n    ALIENS(I,5)=0.5\n  ELSE IF TYPE=2 THEN\n    ALIENS(I,3)=1\n    ALIENS(I,4)=RND*20\n    ALIENS(I,5)=0.35\n  ELSE IF TYPE=3 THEN\n    ALIENS(I,3)=4\n    ALIENS(I,4)=RND*32\n    ALIENS(I,5)=0.25\n  ELSE IF TYPE=4 THEN\n    ALIENS(I,3)=8\n    ALIENS(I,4)=RND*40\n    ALIENS(I,5)=0.15\n  END IF\n  ALIEN=(ALIEN+1) MOD 16\nEND SUB\n\nSUB UPDALIENS\n  FOR I=0 TO 15\n    IF ALIENS(I,0)>0 THEN\n      CALL UPDALIEN(I)\n    END IF\n  NEXT I\nEND SUB\n\nSUB UPDALIEN(I)\n  TYPE=ALIENS(I,0)\n  N=10+I\n  ALIENS(I,1)=ALIENS(I,1)+1\n  T=ALIENS(I,1)\n  X=160-T*ALIENS(I,5)\n  X=X-SIN(T*0.03)*24\n \n  'STILL ON SCREEN?\n  IF X>-32 THEN\n  \n    'UPDATE SPRITE\n    Y=ALIENS(I,2)\n    AY=ALIENS(I,4)\n    IF TYPE MOD 2=0 THEN P=6 ELSE P=7\n    IF TYPE<=2 THEN\n      S=0\n      C=10+INT((T MOD 24)/12)\n      Y=Y+SIN(T/20)*AY\n    ELSE\n      S=1\n      C=6+INT((T MOD 32)/16)*2\n      Y=Y+SIN(T/60)*AY\n    END IF\n    SPRITE N PAL P SIZE S\n    SPRITE N,X,Y,C\n\n    'HIT BY BULLET?\n    IF SPRITE HIT(N,2 TO 9) THEN\n      SPRITE OFF HIT\n      SPRITE N PAL 3\n      ALIENS(I,3)=ALIENS(I,3)-1\n      IF ALIENS(I,3)=0 THEN\n        CALL ADDSCORE(POINTS(TYPE))\n        SPRITE OFF N\n        ALIENS(I,0)=0\n        IF S=0 THEN\n          CALL EXPLODE(X-4,Y-4,0)\n        ELSE\n          CALL EXPLODE(X,Y,0)\n        END IF\n        IF TYPE>=3 THEN PLAY 3,40 SOUND 5\n      ELSE\n        PLAY 3,45 SOUND 2\n      END IF\n    END IF\n  \n    'SHOOT?\n    IF PEACE=0 AND X>50 AND T MOD 120=30 THEN\n      CALL ALIENSHOOT(X+4,Y+4)\n    END IF\n \n  ELSE\n    'OUT OF SCREEN, RESET\n    SPRITE OFF N\n    ALIENS(I,0)=0\n  END IF\nEND SUB\n\nSUB ALIENSHOOT(X,Y)\n  I=ABULLET\n  IF ABULLETS(I,0)>-32 THEN EXIT SUB\n  N=I+30\n  PLAY 3,45+RND*2 SOUND 1\n  SPRITE N,X,Y,4\n  SPRITE N PAL 3\n  ABULLETS(I,0)=X\n  ABULLETS(I,1)=Y\n  U=PX+6-X\n  V=PY+6-Y\n  W=SQR(U*U+V*V)\n  ABULLETS(I,2)=U/W\n  ABULLETS(I,3)=V/W\n  ABULLET=(ABULLET+1) MOD 3\nEND SUB\n\nSUB UPDALIENBULLETS\n  FOR I=0 TO 9\n    N=I+30\n    X=ABULLETS(I,0)\n    Y=ABULLETS(I,1)\n    X=X+ABULLETS(I,2)\n    Y=Y+ABULLETS(I,3)\n    IF X>=-8 AND X<160 AND Y>=-8 AND Y<128 THEN\n      ABULLETS(I,0)=X\n      ABULLETS(I,1)=Y\n      SPRITE N,X,Y,\n    ELSE\n      ABULLETS(I,0)=-32\n      SPRITE OFF N\n    END IF\n  NEXT I\nEND SUB\n\nSUB EXPLODE(X,Y,DELAY)\n  N=EXPLOSION+40\n  SPRITE N PAL 5 SIZE 0\n  SPRITE N,X,Y,0\n  EXPLOSIONS(EXPLOSION)=20+DELAY\n  EXPLOSION=(EXPLOSION+1) MOD 4\nEND SUB\n\nSUB UPDEXPLOSIONS\n  FOR I=0 TO 3\n    T=EXPLOSIONS(I)\n    IF T>0 THEN\n      IF T<=20 THEN\n        N=I+40\n        SPRITE N SIZE 1\n        SPRITE N,,,32+((20-T)\\5)*2\n        IF T=20 THEN PLAY 3,25+RND*5 SOUND 3\n      END IF\n      DEC T\n      IF T=0 THEN SPRITE OFF N\n      EXPLOSIONS(I)=T\n    END IF\n  NEXT I\nEND SUB\n\nSUB DRAWHUD\n  BG 0\n  PAL 4\n  PRIO 1\n  BG FILL 0,0 TO 4,0 CHAR 0\n  FOR I=0 TO LIVES-1\n    CELL I,0,5\n  NEXT I\n  PAL 0\n  NUMBER 15,0,SCORE,5\nEND SUB\n\nSUB DRAWGAMEBG\n  BG SOURCE ROM(3)\n  BG 0\n  BG COPY 0,0,32,16 TO 0,0\n  BG 1\n  BG COPY 0,16,32,16 TO 0,0\nEND SUB\n\nSUB DRAWTITLE\n  BG 0\n  BG FILL 0,0 TO 19,6 CHAR 0\n  BG SOURCE ROM(4)\n  BG COPY 0,0,20,6 TO 4,1\nEND SUB\n\nSUB DRAWGAMEOVER\n  BG 0\n  BG SOURCE ROM(4)\n  BG COPY 0,7,8,2 TO 1,5\n  BG COPY 9,7,8,2 TO 11,5\nEND SUB\n\nSUB CLEAROVERLAYS\n  BG 0\n  BG FILL 0,0 TO 19,6 CHAR 0\n  BG 1\n  BG FILL 0,11 TO 19,11 CHAR 0\nEND SUB\n\nSUB SHOWMSG(MSG$)\n  BG 1\n  PAL 0\n  PRIO 1\n  L=LEN(MSG$)\n  BG FILL 0,11 TO 19,11 CHAR 0\n  TEXT (20-L)/2,11,MSG$\n  MSGTIMER=120\nEND SUB\n\nSUB UPDMSG\n  IF MSGTIMER>0 THEN\n    DEC MSGTIMER\n    IF MSGTIMER=0 THEN\n      BG 1\n      BG FILL 0,11 TO 19,11 CHAR 0\n    END IF\n  END IF\nEND SUB\n\nSUB RESETSOUND\n  STOP\n  FOR I=0 TO 3\n    VOLUME I,15,%11\n  NEXT I\nEND SUB\n\nSUB RASTERFX\n  'STARS AND FOREGROUND HILLS\n  IF RASTER=0 THEN\n    SCROLL 1,BGTICK/6,0\n  ELSE IF RASTER=88 THEN\n    SCROLL 1,0,0\n  ELSE IF RASTER=96 THEN\n    SCROLL 1,BGTICK,0\n  END IF\n\n  'STATUS BAR AND PLANET SURFACE\n  IF RASTER=0 THEN\n    SCROLL 0,0,0\n  ELSE IF RASTER=58 THEN\n    SCROLL 0,BGTICK*0.5,0\n  ELSE IF RASTER=80 THEN\n    SCROLL 0,BGTICK*5/8,0\n  ELSE IF RASTER=96 THEN\n    SCROLL 0,BGTICK*6/8,0\n  ELSE IF RASTER=112 THEN\n    SCROLL 0,BGTICK*7/8,0\n  END IF\nEND SUB\n\n\n#1:MAIN PALETTES\n003F1B0500261101003B2612003F3E39\n001F2A15003C3006003F3121003F1612\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n000040600078FF54000040607078FFFF\n000000000000FC02000000000000FCFE\n04E733040000000006F83C0600000000\n60F0F060000000006090906000000000\n406070821E764040406070FEF0766040\n00041030400C8E0E00071F2F7F73F5F5\n002008040260E1E000E0F8FCFE9E5F5F\n041028400C8E0E06071F377F73F5F5F9\n2008040260E1E0C0E0F8FCFE9E5F5F3F\n0042816600006642007EFFBDFFFF6642\n42816666000066247EFFBDBDFF7E6624\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\nA8FF007802604000FFFF7C787C604000\n7FBDFA7C00000000FFC3867C00000000\n100409A30904100010060EBC0E061000\n40120413041240004012071C07124000\n00000000000000000000000000000000\n040080403C383018FBFFFF7F3C383018\n400001023C1C0C18BFFFFFFE3C1C0C18\n800040203C1C0E02FF7F7F3F3C1C0E02\n010002043C387040FFFEFEFC3C387040\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n000001070F1F1F3F0000010608101020\n000080E0F0F8F8FC0000806010080804\n071F3F7F7FFFFFFF0610204000808000\nE0F8FCFEFEFFFFFF6008040200010100\n071F3F7C78F1E2E404102043078F1E1C\nE0F81C02780102002008E4FEFE030301\n020816285020A000030F1E387060E0C0\n4010280400000000C0F0380400000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n3F1F1F0F070100002010100806010000\nFCF8F8F0E08000000408081060800000\nFFFFFF7F7F3F1F070080800040201006\nFFFFFFFEFEFCF8E00001010002040860\nC0C8C86868301A043838B81858281607\n00000000000000000000000000000000\n0080204020100000C0C0606020100000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00400000000002000000000000000200\n00000000000000000000020000000000\n000008002A0008000000080836080800\n1F604080868C8C8C001F3F7F7E7C7C7C\n6018180C0C000000E0F8F8FCFC000000\n1F604080868C8C8C001F3F7F7F7C7C7C\n6018180C0C0C8C8CE0F8F8FCFCFC7C7C\nEC8C8C8C8C8C8C8C1C7C7C7C7C7C7C7C\n00000000000000000000000000000000\nEC8C8C8C4C4700001C7C7C7C3C3E3F1F\nEC8C8C8C981830601C7C7C7C78F8F0E0\n00000000000000000000000000000000\n00000000000000000000000000000000\nFF80800003232323007F7FFF3F1F1F1F\nFE0000000323232301FFFFFF3F1F1F1F\nC0C0C0C000000000C0C0C0C000000000\n00000000000000000000100000000000\n00000000000004000000000000000400\n00483030480000000078484878000000\n8C8C8C86030000017C7C7C7EFC7F7F1F\nEC8C8C8C0C0C0C0C1C7C7C7CFCFCFCFC\n878080808C8C8C0C787F7F7F7C7C7CFC\n8C0C0C0C8C8C8C8C7CFCFCFC7C7C7C7C\n8C8C8C8C878080007C7C7C7C787F7FFF\n00000000EC0C0C1C000000001CFCFCFC\n2046468C8C8C8C0C1F3F3F7C7C7C7CFC\n3018180C0C8C8C0CF0F8F8FCFC7C7CFC\n0808080808080800070707070707070F\nC0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0\n23232323E18080001F1F1F1F1E7F7FFF\n23232323E00000001F1F1F1F1FFFFFFF\n00000000C0C0C0C000000000C0C0C0C0\n0000000000000000FFFFFFFFFFFFFFFF\n00007EC3C37E0000FFFFFFFCFC81FFFF\n00000000000000000000000000000000\n000000000000082200000000001876DF\n00000000000000000000000000000000\nB0988C878280808070787C7E7D7F7F7F\n2C4C8C0C0C0C0C0C1C3C7CFCFCFCFCFC\nFF8080808C8C8680007F7F7F7C7C797F\nEC0C0C0C0000C0C01CFCFCFC0000C0C0\n8C8C8C07020000007C7C7CFE7D3F1F0F\n8C8C8C0C183060C07C7C7CFCF8F0E0C0\nFF8080808C8C8C8D007F7F7F7D7C7C7C\n6018180C0C0C0C0CE0F8F8FCFCFCFCFC\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0000000038443800FFFFFFFFFFFBC7FF\n00001C0E00000000FFFFFFF1FFFFFFFF\n000001040102164C0103060B1E3D69B3\n0000000000000000FFFFFFFFFFFFFFFF\n00804060B010280480C0E0F0F8FCFEFF\n898C8C8C8C8C8C0C7F7C7C7C7C7C7CFC\n8C8C8C8C8C8C8C0C7C7C7C7C7C7C7CFC\n80808C8C878080007F7F7C7C787F7FFF\nC0C00000EC0C0C0CC0C000001CFCFCFC\n8C8C8C87020000007C7C7C7EFD7F7F1F\n8C8C8C0C0C1818607C7C7CFCFCF8F8E0\n868080808C8C8C0C797F7F7F7F7D7CFC\n18186030180C0C0CF8F8E0F0F8FCFCFC\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00181818180018000000000000000000\n006C6C24000000000000000000000000\n00247E24247E24000000000000000000\n00083E380E3E08000000000000000000\n00626408102646000000000000000000\n001C34386E643A000000000000000000\n00181830000000000000000000000000\n000C183030180C000000000000000000\n0030180C0C1830000000000000000000\n000024187E1824000000000000000000\n000018187E1818000000000000000000\n00000000181830000000000000000000\n000000007E0000000000000000000000\n00000000001818000000000000000000\n00060C18306040000000000000000000\n003C666E76663C000000000000000000\n0018381818187E000000000000000000\n003C660C18307E000000000000000000\n003C660C06663C000000000000000000\n0066667E060606000000000000000000\n007E607C06067C000000000000000000\n001C307C66663C000000000000000000\n007E060C183030000000000000000000\n003C663C66663C000000000000000000\n003C663E06663C000000000000000000\n00000018001800000000000000000000\n00000018001830000000000000000000\n00000C1830180C000000000000000000\n0000007E007E00000000000000000000\n000030180C1830000000000000000000\n003C660C180018000000000000000000\n003C666E6E603C000000000000000000\n00183C667E6666000000000000000000\n007C667C66667C000000000000000000\n003C666060663C000000000000000000\n00786C66666C78000000000000000000\n007E607860607E000000000000000000\n007E6078606060000000000000000000\n003C606E66663C000000000000000000\n0066667E666666000000000000000000\n003C181818183C000000000000000000\n001E060606663C000000000000000000\n00666C78786C66000000000000000000\n0060606060607E000000000000000000\n0042667E7E6666000000000000000000\n0066767E6E6666000000000000000000\n003C666666663C000000000000000000\n007C667C606060000000000000000000\n003C66666A6C3E000000000000000000\n007C667C786C66000000000000000000\n003E603C06067C000000000000000000\n007E1818181818000000000000000000\n0066666666663C000000000000000000\n00666666663C18000000000000000000\n0066667E7E6642000000000000000000\n00663C183C6666000000000000000000\n0066663C181818000000000000000000\n007E0C1830607E000000000000000000\n003C303030303C000000000000000000\n006030180C0602000000000000000000\n003C0C0C0C0C3C000000000000000000\n00183C66000000000000000000000000\n0000000000007E000000000000000000\n\n#3:MAIN BG\n00002020000000000000002100210021\n00210000002100210021000000000000\n00000021002100210021002100000021\n00210000000000210021002100210021\n00210021000000000000002100210000\n00210000002100210021000000000000\n00000021002100210000002100000021\n00000021000000000021002100210021\n00210021002100000000000000210021\n00210021002100210021000000210000\n00210021002100000021002100000021\n00210021002100000000002100000021\n00000021000000210000000000000000\n00000000002100210021000000000021\n00000021000000000021002100000000\n00000000000000000021002100210000\n00210021000000000000002100210021\n00210021000000210021002100000021\n00210021000000210021002100000021\n00210021002100000021002100210000\n00000000000000000000002100210021\n00210000002100210021002100210021\n00210000000000210021002100000021\n00210021002100000000002100210021\n00000000002100210021000000000021\n00210021000000000021002100210021\n00000000002100210021002100000021\n00210021002100210000002100210021\n00210021002100210021002100210021\n00210021002100216201630164010021\n00210021000000210021002100210021\n00006201630164010021002100210021\n00210001000100016201630164010001\n62010001640163017201730174010001\n00016201630164010021002100010001\n63017201730174016301002100216201\n63016401630163017201730174016301\n63016301720173017301730173017401\n63017201730174016301630163017201\n73017301730173017301740163017201\n73017401600160016001600160016001\n60016001710160017301730173016001\n60016001710160016001600160016001\n71016001600160016001600160016001\n60016001700160016001710170017101\n70016001710160017001600170017101\n70016001600171016001600160017101\n60016001710160016001710160016001\n71016001600160016001710160016001\n60016001600160016001600160016001\n60017001610160016001710170016001\n60016001700160016001600160016001\n60016001700171016001600160017001\n60016101700171016001600170016001\n60016001700160017101600160016001\n61016001600170016001710170017101\n70017101600160017001600160016001\n60017001600160016001610160017001\n60016101600160016001700160017101\n60016001600161016001700160016001\n60016101700160016001610160017001\n60016001600170016001600160017101\n70017101600171017001710170016001\n70017101700160016001600161016001\n70017101400041000000000000000000\n51000000000000005000000000000000\n50000000000000000000000000000000\n00000000000000000000500000000000\n40004100500051000000400041000000\n00000000000000000000510000000000\n00005100000000004000410000000000\n00004000410000000000000000000000\n50004200000051000000500051000000\n50000000420041000000000040004100\n00000000510000005000510000000000\n00005000510000000000400041000000\n00000000000000000000000051000000\n51000000500051005000000050005100\n00005100000000000000000000000000\n00000000000000005100500051000000\n51000000000040004100000000000000\n00000000000000000000000000000000\n00004200000041000000000000005100\n40004100000000005201000000000000\n00000000000050005100500000004000\n50000000000040004100410000000000\n00000000510000000000500000000000\n50005100000000000000400041000000\n00000000000000005000000000005000\n51000000000050005100000000004000\n50000000500000005100000000005000\n00000000000051000000500051000000\n00000000000000005000410000000000\n00004000410000000000000000005000\n51000000000000000000000051000000\n00005100000050000000000000004000\n41000000500000005000420000005100\n62010001640100005201510000005100\n50005100000040004100000000004200\n00000000000000000000510000005000\n51000000510000000000000062016201\n00010001000100005100000000000000\n00000000000050005100000051000000\n50000000000000005100000051000000\n00000000400062016201620100010001\n00010001500000000000000062220002\n64225100000051000000000000000000\n00000000400041000000000000000000\n00000000500000010001000100010001\n41000000000000010001000100020002\n64020001000100010001000100010001\n00010001000100010001000100010001\n00010000000000000000000000000001\n00210020002000220002002200226322\n00220002000000000000000100010001\n00010001000000000000000100010001\n00010000000000006221632264220020\n00000002000262020002002272227322\n74220022000200016222632264220000\n00000000000000000000000062226322\n64010000000000027222732274220020\n00210022000200226322722270227322\n73227422002200027222732274226322\n63226402000062026322632272227322\n74220022002272227322712273227422\n00220002002272227322732273226122\n73227122742272227322732273227322\n73227422002072227322732271227322\n73227422\n\n#4:OVERLAY BG\n00001410000000000000AC20AF20B720\nB220A520B32000000000000000000000\n00000000000000000000000043204420\n45204620472048204520462049204A20\n49204A20000000000000000000000000\n00000000532054205520562057205820\n5520562059205A205B205C2000000000\n00000000000000000000000000000000\n00000000000200020002000200000000\n00000000000000000000000000000000\n00000000000000000000000200024D22\n4E224F22000000000000000000000000\n00000000000000000000000000000000\n0000000000025D225E225F2200000000\n00000000000000000000000000000000\n00000000000000000000000000020000\n00000000000000000000000000000000\n00000000000000000000000043254425\n45254625652566256725682580254525\n462547254725672568256B256C250000\n00000000532554255525562575257625\n77257825002579257A2569256A257725\n78257B257C2500000000000000000005\n00050000000000000000000000000000\n00000005000500250025000500050005\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000\n\n#14:SFX\n5801505019FC00005801506019FB0000\n4801004F19FF00007806007F01FE0000\n780A04AF19FD00007805027F19FD0000\n0800505012FF00000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n296FE6306F00356F002A6F00316F0036\n6F002C6F00336F00386F00FF00000000\nE0000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n\n#15:MAIN SOUND\n230030AA0004A0002800606019000000\n22006060000250002800303019FE0000\n38002020000000003800606000000000\n21004068120B30002400418A10372000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n800103180002041A000103180002041A\n000116180002171A000116180082171A\n85064040050B4040090A4040090C4040\n05064040050B40400D0E40400D0F4040\n11104040111040401314404013144040\n13154040139540404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n87884040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n193F00000000193F00000000315F0019\n3F00000000193F00000000554500193F\n00000000315F00000000000000000000\n193F00000000193F00000000315F0019\n3F00000000193F00000000000000193F\n00554500315F00000000554500554800\n1D0F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n1B0F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n190F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n1B0F0000000000000000000000000000\n00000000000000000000000000000000\n00000000180F00000000000000000000\n356F00000000356F00000000386F0000\n0000356F00000000000000000000386F\n00000000FF0000000000356F00000000\n3C6F000000000000000000003A6F0000\n0000386F000000000000000000003A6F\n00000000000000FF0000000000000000\n3D6F000000000000000000003C6F0000\n00003A6F00000000FF0000000000356F\n00000000000000000000FF0000000000\n386F00000000386F000000003A6F0000\n0000386F00000000000000000000376F\n00000000000000000000FF0000000000\n1B2FE81B2F000000001B2F001C2F0000\n00001B2F001B2F00000000202F001B2F\n001B2F001E2F001B2F001C2F001B2F00\n1B2F001B2F000000001B2F001C2F0000\n00001B2F001B2F00000000202F001B2F\n001B2F001E2F001B2F001C2F001B2F00\n337800000000000000FF000000000000\n00002E7800000000000000FF00000000\n00000000337800000000000000000000\n31780000000030780000000000000000\n00002E7800000000000000FF00000000\n000000002C7800000000000000000000\n190F0000000000000000000000000000\n0000000000000000FF00000000000000\n00000000000000000000000000000000\n190F0000000000000000000000000000\n0000000000000000FF00000000000000\n00000000000000000000000000000000\n4278003F78003A78003678003378002E\n78002A7800277800FF00000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n1E2F001E2F001E2F00000000192F0000\n0000192F001E2F000000001F2F000000\n001F2F000000001F2F000000001F2F00\n1E2F001E2F001E2F00000000192F0000\n0000192F001E2F000000001F2F000000\n001F2F000000001F2F001A2F001C2F00\n36780000000000000037780000000000\n00003678000000000000003778000000\n00000000367800000000FF0000000000\n2A78000000000000002B780000000000\n00002C78000000000000002D78000000\n000000002E78000000002F7800000000\n2E7800000000000000FF000000000000\n00002A7800000000000000FF00000000\n00000000257800000000000000000000\n277800000000000000000000FF000000\n00000000002C78000000000000002D78\n00000000317800000000FF0000000000\n36780000000000000037780000000000\n00003678000000000000003778000000\n00000000367800000000FF0000000000\n2A78000000000000002B780000000000\n00003078000000000000002F78000000\n000000002E78000000002D7800000000\n1E2F00000000192F000000001E2F0000\n0000192F001C2F000000001C2F001B2F\n00000000192F00000000172F00000000\n152F00152F00000000172F00172F0000\n0000192F00192F000000001C2F001C2F\n000000001B2F00000000192F00000000\n36780000000000000038780000000000\n0000397800000000000000FF00000000\n00000000000000000000000000000000\n34780000000000000033780000000000\n00003178000000000000002F78000000\n00000000317800000000FF0000000000\n36780000000000000038780000000000\n0000397800000000000000000000FF00\n00000000000000000000000000000000\n2D78000000000000002F780000000000\n00003178000000000000003478000000\n00000000317800000000FF0000000000\n33780000000000000000000000000000\n0000000000000000000000FF00000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n1B2F001B2F000000001B2F001C2F0000\n00001B2F001B2F00000000202F001B2F\n000000001C2F000000001C2F00000000\n1B2F001B2F00000000162F00162F0000\n0000172F00172F00000000122F00122F\n00000000122F00000000122F00000000\n1B2F001B2F000000000000001B2F0000\n00000000000000001B2F000000000000\n000000001B2F00000000000000000000\n122F00122F000000000000000F2F0000\n00000000000000001B2F000F2F000000\n000000001B2F000F2F00000000000000\n1B0FD000000000000000000000000000\n0000000000000000120F000000000000\n00000000000000000000000000000000\n190F0000000000000000000000000000\n0000000000000000140F000000000000\n00000000000000000000FF0000000000\n2A7800000000000000000000FF000000\n00000000000000002E78000000000000\n00000000FF0000000000000000000000\n2C780000000000000000000025780000\n00000000000000002778000000000000\n00000000FF0000000000000000000000\n2E7800000000000000000000FF000000\n00000000000000003178000000000000\n00000000FF0000000000000000000000\n3678000000000000000000003A780000\n00000000000000003378000000000000\n00000000FF0000000000000000000000\n416F00000000356F000000003C6F0000\n0000416F00000000000000FF00000000\n00000000000000000000000000000000\n436F00000000376F00000000446F0000\n0000466F00000000000000FF00000000\n00000000000000000000000000000000\n3D6F00000000316F00000000FF000000\n00003D6F00000000316F00000000FF00\n00000000000000000000000000000000\n436F00000000436F00000000446F0000\n0000436F000000003F6F000000000000\n00FF0000000000000000000000000000\n2918012C1A02291C01291C022C1C0129\n1C02291A012C18020000000000002918\n012C1A02291C01291A022C1801000000\n271801301A02271C01271C02301C0127\n1C02271A013018020000000000002718\n01301A02271C01271A02301801000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n251801291A02251C01251C02291C0125\n1C02291A012518020000000000002518\n01291A02251C01251A02291801000000\n271801271A022C1C01271C022C1C012C\n1C02271A012C18020000000000002718\n012C1A02271C01271A022C1801000000\n\n"
  },
  {
    "path": "programs/Sound Composer 1.5.nx",
    "content": "'TITLE:   SOUND COMPOSER\n'AUTHOR:  TIMO KLOSS\n\n'ENERGY SAVING MODE\nSYSTEM 0,1\n\nGLOBAL NUMINST,INSTSIZE\nNUMINST=16\nINSTSIZE=8\n\nGLOBAL NUMPATT,PATTSIZE\nNUMPATT=64\nPATTSIZE=4\n\nGLOBAL NUMTRAC,NUMROWS,ROWSIZE,TRACSIZE\nNUMTRAC=64\nNUMROWS=32\nROWSIZE=3\nTRACSIZE=NUMROWS*ROWSIZE\n\nGLOBAL SONGSIZE\nSONGSIZE=NUMINST*INSTSIZE + NUMPATT*PATTSIZE + NUMTRAC*TRACSIZE\n\n'==== MEMORY MAP ====\n\nGLOBAL AINST,APATT,ATRAC\nAINST=$A000\nAPATT=AINST+NUMINST*INSTSIZE\nATRAC=APATT+NUMPATT*PATTSIZE\nACOPYINST=ATRAC+NUMTRAC*TRACSIZE\nACOPYPATT=ACOPYINST+INSTSIZE\nACOPYTRAC=ACOPYPATT+PATTSIZE\nIF ACOPYTRAC+TRACSIZE>$E000 THEN END\n\n'======================\n\nDIM GLOBAL MENU$(6)\n\n'EDIT FOR DIFFERENT KEYBOARD LAYOUTS\nGLOBAL KEYS$, VALKEYS$\nKEYS$=\"ZSXDCVGBHNJMQ2W3ER5T6Y7UI\"\nVALKEYS$=\"0123456789ABCDEF\"\n\nDIM GLOBAL NOTES$(11), HEXS$(15), SNDWAV$(3), LFOWAV$(3), FLAG$(1)\nDATA \"C-\",\"C#\",\"D-\",\"D#\",\"E-\",\"F-\"\nDATA \"F#\",\"G-\",\"G#\",\"A-\",\"A#\",\"B-\"\nFOR I=0 TO 11\n  READ NOTES$(I)\nNEXT I\n'CACHE HEX VALUES 0-F\nFOR I=0 TO 15\n  HEXS$(I)=HEX$(I)\nNEXT I\nDATA \"SAW\",\"TRI\",\"PUL\",\"NOI\"\nFOR I=0 TO 3\n  READ SNDWAV$(I)\nNEXT I\nDATA \"TRI\",\"SAW\",\"SQR\",\"RND\"\nFOR I=0 TO 3\n  READ LFOWAV$(I)\nNEXT I\nFLAG$(0)=\"OFF\"\nFLAG$(1)=\"ON \"\n\nGLOBAL METERSENABLED, OLDX, OLDY\n\nGLOBAL CURPAT, CURVOI, CURROW, CURTRA, CURCOL, CUROCT, CURSND, CURNOTE, FOCUS, SELTAB\nCALL INITSTATUS\n\nSUB INITSTATUS\n  CURPAT=0\n  CURVOI=0\n  CURROW=0\n  CURTRA=0\n  CURCOL=0\n  CUROCT=3\n  CURSND=0\n  CURNOTE=46\n  FOCUS=-1\n  SELTAB=0\nEND SUB\n\nGLOBAL SOUNDFILE,FILESOFFS\n\nCALL CLEARMUSIC\n\n'LOAD MAIN SOUND\nSOUNDFILE=15\nFILESOFFS=5\nOK=0\nR=0\nUPD=0\nCALL BLOAD(OK)\n\nKEYBOARD OPTIONAL\n\n'==== PLAYER ====\n\nGLOBAL PLAYER_MODE, PLAYER_SPEED, PLAYER_TICK, PLAYER_PAT, PLAYER_ROW, PLAYER_BREAK\nGLOBAL PLAYER_TRA, PLAYER_VOI\nON VBL CALL UPDATEPLAYER\n\n'==== TOUCH ZONES INIT ====\n\n'SETTINGS\nGLOBAL MAX_ZONE, ZONE_PAL\n'STATUS GETTERS\nGLOBAL CUR_ZONE, ZONE_EVENT, ZONE_IN_X, ZONE_IN_Y, ZONE_RESULT\n'INTERNAL\nGLOBAL ZONE_LAST_X, ZONE_LAST_Y\n\nGLOBAL E_DOWN, E_UP, E_OUT, E_DRAG\nE_DOWN=1\nE_UP=2\nE_OUT=3\nE_DRAG=4\n\nMAX_ZONE=35\nDIM GLOBAL ZONEX(MAX_ZONE), ZONEY(MAX_ZONE), ZONEW(MAX_ZONE), ZONEH(MAX_ZONE), ZONEP(MAX_ZONE)\n\nTOUCHSCREEN\n\n'========\n\nMAIN:\nIF SELTAB=0 THEN GOTO PATTERNEDITOR\nIF SELTAB=1 THEN GOTO TRACKEDITOR\nIF SELTAB=2 THEN GOTO SOUNDEDITOR\nEND\n\n'========\n\nPATTERNEDITOR:\n\nSELTAB=0\n\nSPRITE OFF\nSPRITE 0 PAL 3 FLIP 0,0\nSPRITE 1 PAL 3 FLIP 1,0\nSPRITE 2 PAL 3 FLIP 0,1\nSPRITE 3 PAL 3 FLIP 1,1\nCALL METERSON\n\nCALL RESETZONES\n\nBG SOURCE ROM(3)\nBG COPY 0,0,20,2 TO 0,0\n\nZONE_PAL=1\nCALL SETZONE(0,0,0,2,2)\nCALL SETZONE(1,2,0,2,2)\nCALL SETZONE(2,4,0,2,2)\nCALL SETZONE(3,6,0,2,2)\nCALL SETZONE(4,8,0,2,2)\nCALL SETZONE(5,10,0,2,2)\nZONE_PAL=-1\nCALL SETZONE(6,12,0,2,2)\nCALL SETZONE(7,14,0,2,2)\nCALL SETZONE(8,16,0,2,2)\nZONE_PAL=1\nCALL SETZONE(9,18,0,2,2)\n\nZONE_PAL=-1\nCALL SETZONE(10,0,2,2,2)\nCALL SETZONE(11,3,2,3,2)\nCALL SETZONE(12,7,2,3,2)\nCALL SETZONE(13,11,2,3,2)\nCALL SETZONE(14,15,2,3,2)\nZONE_PAL=1\nCALL SETZONE(15,18,2,2,2)\n\nZONE_PAL=-1\nCALL SETZONE(30,0,4,20,10)\n\nFOCUS=-1\nCALL DRAWPATINFO\nCALL DRAWPATTERN\nCALL SHOWKEYBOARD\nCALL UPDATEPATCURSOR\n\nOLDX=-1\nOLDY=-1\nDRAGGED=0\n\nDO\n  CALL UPDATEZONES\n  \n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=0 THEN CALL PLAYMUSIC(CURPAT,1)\n    IF CUR_ZONE=1 THEN CALL PLAYMUSIC(CURPAT,2)\n    IF CUR_ZONE=2 THEN\n      STOP\n      IF PLAYER_MODE>0 THEN\n        CALL STOPPLAYER\n      ELSE IF CURROW>0 THEN\n        CURROW=0\n        CALL DRAWPATTERN\n      ELSE\n        CURPAT=CURPAT-1\n        CALL GETLOOPSTART(CURPAT)\n        CALL DRAWPATINFO\n        CALL DRAWPATTERN\n      END IF\n    END IF\n    IF CUR_ZONE=5 THEN GOTO PATTERNMENU\n    IF CUR_ZONE=9 THEN GOTO DISKMENU\n  END IF\n\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=3 AND CURPAT>0 THEN\n      CURPAT=CURPAT-1\n      CALL CHANGEMUSIC(CURPAT)\n      CALL DRAWPATINFO\n      CALL DRAWPATTERN\n    END IF\n    IF CUR_ZONE=4 AND CURPAT<NUMPATT-1 THEN\n      CURPAT=CURPAT+1\n      CALL CHANGEMUSIC(CURPAT)\n      CALL DRAWPATINFO\n      CALL DRAWPATTERN\n    END IF\n    IF CUR_ZONE=7 THEN GOTO TRACKEDITOR\n    IF CUR_ZONE=8 THEN GOTO SOUNDEDITOR\n  END IF\n\n  IF CUR_ZONE=30 THEN\n    IF ZONE_EVENT=E_DOWN THEN\n      IF FOCUS<>-1 THEN\n        FOCUS=-1\n        CALL SHOWKEYBOARD\n        CALL UPDATEPATCURSOR\n      END IF\n      OLDY=ZONE_IN_Y\n      DRAGGED=0\n    ELSE IF ZONE_EVENT=E_DRAG THEN\n      IF PLAYER_MODE<>1 AND PLAYER_MODE<>2 THEN\n        CURROW=MIN(NUMROWS-1,MAX(0,CURROW+OLDY-ZONE_IN_Y))\n        CALL DRAWPATTERN\n        CALL UPDATEPATCURSOR\n        OLDY=ZONE_IN_Y\n      END IF\n      DRAGGED=-1\n    ELSE IF ZONE_EVENT=E_UP THEN\n      IF NOT DRAGGED THEN\n        CURVOI=MIN(3,MAX(0,(ZONE_IN_X-3)\\4))\n        IF PLAYER_MODE<>1 AND PLAYER_MODE<>2 THEN\n          CURROW=MIN(NUMROWS-1,MAX(0,CURROW+ZONE_IN_Y-4))\n        END IF\n        CALL DRAWPATTERN\n        CALL UPDATEPATCURSOR\n      END IF\n    ELSE IF ZONE_EVENT=E_OUT THEN\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_DOWN AND CUR_ZONE>=10 AND CUR_ZONE<=14 THEN\n    FOCUS=CUR_ZONE\n    IF FOCUS>=11 AND FOCUS<=14 THEN CURVOI=FOCUS-11\n    CALL SHOWWHEEL\n    CALL UPDATEPATCURSOR\n  END IF\n  IF ZONE_EVENT=E_UP AND CUR_ZONE=15 THEN\n    CALL TOGGLELOOP\n    CALL DRAWLOOP\n  END IF\n\n  K$=INKEY$\n  IF K$=\" \" THEN\n    STOP\n    IF PLAYER_MODE>0 THEN\n      CALL STOPPLAYER\n    ELSE\n      CALL PLAYMUSIC(CURPAT,2)\n    END IF\n  ELSE IF K$<>\"\" AND FOCUS=-1 THEN\n    K=ASC(K$)\n    IF K=17 THEN\n      CURVOI=(CURVOI+1) MOD 4\n      CALL UPDATEPATCURSOR\n    ELSE IF K=18 THEN\n      CURVOI=(CURVOI+3) MOD 4\n      CALL UPDATEPATCURSOR\n    END IF\n    IF PLAYER_MODE<>1 AND PLAYER_MODE<>2 THEN\n      IF K=19 THEN\n        CURROW=(CURROW+1) MOD NUMROWS\n        CALL DRAWPATTERN\n      ELSE IF K=20 THEN\n        CURROW=(CURROW+NUMROWS-1) MOD NUMROWS\n        CALL DRAWPATTERN\n      END IF\n    END IF\n  END IF\n\n  VOIX=3+CURVOI*4\n  CALL GETTRACK(CURPAT,CURVOI,CURTRA)\n\n  CALL HANDLEKEYBOARD(0,VOIX,K$,UPD)\n  IF K$<>\"\" AND UPD AND PLAYER_MODE<>1 AND PLAYER_MODE<>2 THEN\n    CURROW=(CURROW+1) MOD NUMROWS\n    CALL DRAWPATTERN\n  END IF\n\n  IF FOCUS=10 THEN\n    CALL HANDLEWHEEL(CURPAT,0,NUMPATT-1,K$,UPD)\n    IF UPD THEN\n      CALL DRAWPATINFO\n      CALL DRAWPATTERN\n    END IF\n  END IF\n\n  IF FOCUS>=11 AND FOCUS<=14 THEN\n    CALL HANDLEWHEEL(CURTRA,-1,NUMTRAC-1,K$,UPD)\n    IF UPD THEN\n      CALL SETTRACK(CURPAT,CURVOI,CURTRA)\n      CALL DRAWPATINFO\n      CALL DRAWTRACK(CURTRA,VOIX)\n    END IF\n  END IF\n\n  IF PLAYER_MODE=1 OR PLAYER_MODE=2 THEN\n    IF PLAYER_ROW<>CURROW OR PLAYER_PAT<>CURPAT THEN\n      P=CURPAT\n      CURROW=PLAYER_ROW\n      CURPAT=PLAYER_PAT\n      IF CURPAT<>P THEN CALL DRAWPATINFO\n      CALL DRAWPATTERN\n    END IF\n  END IF\n\n  WAIT VBL\nLOOP\n\n\nPATTERNMENU:\nMENU$(0)=\"COPY\"\nMENU$(1)=\"PASTE\"\nMENU$(2)=\"INSERT EMPTY\"\nMENU$(3)=\"REMOVE\"\nMENU$(4)=\"CANCEL\"\nCALL SHOWMENU(\"EDIT PATTERN\",4,R)\nIF R=0 THEN COPY APATT+CURPAT*PATTSIZE,PATTSIZE TO ACOPYPATT\nIF R=1 THEN COPY ACOPYPATT,PATTSIZE TO APATT+CURPAT*PATTSIZE\nIF R=2 THEN\n  COPY APATT+CURPAT*PATTSIZE,(NUMPATT-CURPAT-1)*PATTSIZE TO APATT+(CURPAT+1)*PATTSIZE\n  FILL APATT+CURPAT*PATTSIZE,PATTSIZE,64\nEND IF\nIF R=3 THEN\n  COPY APATT+(CURPAT+1)*PATTSIZE,(NUMPATT-CURPAT-1)*PATTSIZE TO APATT+CURPAT*PATTSIZE\n  FILL APATT+(NUMPATT-1)*PATTSIZE,PATTSIZE,64\nEND IF\nGOTO PATTERNEDITOR\n\nSUB DRAWPATTERN\n  PAL 3\n  BG FILL 0,4 TO 20,13 CHAR 0\n  CALL DRAWPOS\n  T=-1\n  FOR V=0 TO 3\n    CALL GETTRACK(CURPAT,V,T)\n    CALL DRAWTRACK(T,3+V*4)\n  NEXT V\nEND SUB\n\nSUB DRAWPATINFO\n  PAL 0\n  BG FILL 0,2 TO 19,3 CHAR 1\n  TEXT 0,2,\"PA\"\n  PAL 2\n  NUMBER 0,3,CURPAT,2\n  T=-1\n  FOR V=0 TO 3\n    CALL GETTRACK(CURPAT,V,T)\n    X=3+V*4\n    PAL 0\n    TEXT X,2,\"V\"\n    NUMBER X+1,2,V,1\n    TEXT X,3,\"T\"\n    PAL 2\n    IF T<0 THEN\n      TEXT X+1,3,\"--\"\n    ELSE\n      NUMBER X+1,3,T,2\n    END IF\n  NEXT V\n  CALL DRAWLOOP\nEND SUB\n\nSUB DRAWLOOP\n  V=0\n  CALL GETLOOP(CURPAT,0,V)\n  FLIP 0,1\n  IF V=1 THEN PAL 0 ELSE PAL 2\n  CELL 18,2,46\n  CALL GETLOOP(CURPAT,1,V)\n  FLIP 1,0\n  IF V=1 THEN PAL 0 ELSE PAL 2\n  CELL 19,2,46\n  CALL GETLOOP(CURPAT,2,V)\n  FLIP 0,0\n  IF V=1 THEN PAL 0 ELSE PAL 2\n  CELL 19,3,47\nEND SUB\n\nSUB TOGGLELOOP\n  V1=0\n  V2=0\n  V3=0\n  CALL GETLOOP(CURPAT,0,V1)\n  CALL GETLOOP(CURPAT,1,V2)\n  CALL GETLOOP(CURPAT,2,V3)\n  V=V1+V2*2+V3*4\n  V=(V+1) MOD 5\n  CALL SETLOOP(CURPAT,0,V MOD 2)\n  CALL SETLOOP(CURPAT,1,V\\2 MOD 2)\n  CALL SETLOOP(CURPAT,2,V\\4 MOD 2)\nEND SUB\n\nSUB DRAWPOS\n  PAL 1\n  BG FILL 0,8 TO 19,8 CHAR 1\n  FOR I=0 TO 9\n    P=I+CURROW-4\n    IF P>=0 AND P<NUMROWS THEN\n      IF I=4 THEN PAL 1 ELSE PAL 3\n      NUMBER 0,4+I,P,2\n    END IF\n  NEXT I\nEND SUB\n\nSUB DRAWTRACK(TRA,X)\n  FOR I=0 TO 9\n    R=I+CURROW-4\n    IF R>=0 AND R<NUMROWS THEN\n      CALL DRAWNOTE(TRA,R,X,4+I)\n    END IF\n  NEXT I\nEND SUB\n\nSUB DRAWNOTE(TRA,R,X,Y)\n  IF R=CURROW THEN PAL 1 ELSE PAL 3\n  IF TRA<0 THEN\n    TEXT X,Y,\" - \"\n  ELSE\n    NOTE=0\n    SND=0\n    VOL=0\n    COM=0\n    PAR=0\n    CALL GETROW(TRA,R,NOTE,SND,VOL,COM,PAR)\n    IF NOTE=0 THEN\n      TEXT X,Y,\"---\"\n    ELSE IF NOTE=255 THEN\n      TEXT X,Y,\"-*-\"\n    ELSE\n      TEXT X,Y,NOTES$((NOTE-1) MOD 12)\n      NUMBER X+2,Y,(NOTE-1)\\12,1\n    END IF\n  END IF\nEND SUB\n\nSUB DRAWOCTAVE\n  PAL 0\n  CELL 4,15,56+CUROCT\nEND SUB\n\nSUB UPDATEPATCURSOR\n  IF FOCUS>=0 THEN\n    CALL SETCURSOR(ZONEX(FOCUS), ZONEY(FOCUS), ZONEW(FOCUS), ZONEH(FOCUS))\n  ELSE\n    CALL SETCURSOR(3+CURVOI*4,8,3,1)\n  END IF\nEND SUB\n\nSUB SETCURSOR(X,Y,W,H)\n  SPRITE 0,X*8-1,Y*8-1,32\n  SPRITE 1,(X+W)*8-7,Y*8-1,32\n  SPRITE 2,X*8-1,(Y+H)*8-7,32\n  SPRITE 3,(X+W)*8-7,(Y+H)*8-7,32\nEND SUB\n\nSUB PREPARETRACK(TRA,VOIX)\n  IF TRA=-1 THEN\n    CALL GETFREETRACK(TRA)\n    CALL SETTRACK(CURPAT,CURVOI,TRA)\n    CALL DRAWPATINFO\n    CALL DRAWTRACK(TRA,VOIX)\n  END IF\nEND SUB\n\n'===================================\n\nTRACKEDITOR:\n\nSELTAB=1\n\nSPRITE OFF\nCALL METERSON\n\nCALL RESETZONES\n\nBG SOURCE ROM(3)\nBG COPY 0,8,20,2 TO 0,0\n\nZONE_PAL=1\nCALL SETZONE(0,0,0,2,2)\nCALL SETZONE(1,2,0,2,2)\nCALL SETZONE(2,4,0,2,2)\nCALL SETZONE(3,6,0,2,2)\nCALL SETZONE(4,8,0,2,2)\nZONE_PAL=-1\nCALL SETZONE(6,12,0,2,2)\nCALL SETZONE(7,14,0,2,2)\nCALL SETZONE(8,16,0,2,2)\nZONE_PAL=1\nCALL SETZONE(9,18,0,2,2)\n\nZONE_PAL=-1\nCALL SETZONE(10,0,2,2,2)\n\nZONE_PAL=-1\nCALL SETZONE(30,0,4,20,10)\n\nFOCUS=-1\nCURCOL=0\nIF CURTRA=-1 THEN CURTRA=0\nCALL DRAWTRAINFO\nCALL DRAWFULLTRACK\nCALL UPDATETRACURSOR\nCALL UPDATETRAINPUT\n\nOLDX=-1\nOLDY=-1\nDRAGGED=0\n\nDO\n  CALL UPDATEZONES\n  \n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=0 THEN CALL PLAYTRACK(CURTRA,CURVOI)\n    IF CUR_ZONE=1 THEN\n      STOP\n      IF PLAYER_MODE>0 THEN\n        CALL STOPPLAYER\n      ELSE IF CURROW>0 THEN\n        CURROW=0\n        CALL DRAWFULLTRACK\n      END IF\n    END IF\n    IF CUR_ZONE=4 THEN GOTO TRACKMENU\n    IF CUR_ZONE=9 THEN GOTO DISKMENU\n  END IF\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=2 AND CURTRA>0 THEN\n      CURTRA=CURTRA-1\n      CALL CHANGETRACK(CURTRA)\n      CALL DRAWTRAINFO\n      CALL DRAWFULLTRACK\n    END IF\n    IF CUR_ZONE=3 AND CURTRA<NUMTRAC-1 THEN\n      CURTRA=CURTRA+1\n      CALL CHANGETRACK(CURTRA)\n      CALL DRAWTRAINFO\n      CALL DRAWFULLTRACK\n    END IF\n    IF CUR_ZONE=6 THEN GOTO PATTERNEDITOR\n    IF CUR_ZONE=8 THEN GOTO SOUNDEDITOR\n  END IF\n\n  IF ZONE_EVENT=E_DOWN AND CUR_ZONE=10 THEN\n    FOCUS=CUR_ZONE\n    CALL SHOWWHEEL\n    CALL UPDATETRACURSOR\n  END IF\n\n  IF CUR_ZONE=30 THEN\n    IF ZONE_EVENT=E_DOWN THEN\n      IF FOCUS<>-1 THEN\n        FOCUS=-1\n        CALL UPDATETRACURSOR\n      END IF\n      OLDY=ZONE_IN_Y\n      DRAGGED=0\n    ELSE IF ZONE_EVENT=E_DRAG THEN\n      IF PLAYER_MODE<>3 THEN\n        CURROW=MIN(NUMROWS-1,MAX(0,CURROW+OLDY-ZONE_IN_Y))\n        CALL DRAWFULLTRACK\n        CALL UPDATETRACURSOR\n        OLDY=ZONE_IN_Y\n      END IF\n      DRAGGED=-1\n    ELSE IF ZONE_EVENT=E_UP THEN\n      IF NOT DRAGGED THEN\n        IF ZONE_IN_X<7 THEN\n          CURCOL=0\n        ELSE\n          CURCOL=MIN(4,1+(ZONE_IN_X-7)\\2)\n        END IF\n        IF PLAYER_MODE<>3 THEN\n          CURROW=MIN(NUMROWS-1,MAX(0,CURROW+ZONE_IN_Y-4))\n        END IF\n        CALL DRAWFULLTRACK\n        CALL UPDATETRACURSOR\n      END IF\n      CALL UPDATETRAINPUT\n    END IF\n  END IF\n\n  K$=INKEY$\n  IF K$=\" \" THEN\n    STOP\n    IF PLAYER_MODE>0 THEN\n      CALL STOPPLAYER\n    ELSE\n      CALL PLAYTRACK(CURTRA,CURVOI)\n    END IF\n  ELSE IF K$<>\"\" AND FOCUS=-1 THEN\n    K=ASC(K$)\n    IF K=17 THEN\n      CURCOL=(CURCOL+1) MOD 5\n      CALL UPDATETRACURSOR\n      CALL UPDATETRAINPUT\n    ELSE IF K=18 THEN\n      CURCOL=(CURCOL+4) MOD 5\n      CALL UPDATETRACURSOR\n      CALL UPDATETRAINPUT\n    END IF\n    IF PLAYER_MODE<>3 THEN\n      IF K=19 THEN\n        CURROW=(CURROW+1) MOD NUMROWS\n        CALL DRAWFULLTRACK\n      ELSE IF K=20 THEN\n        CURROW=(CURROW+NUMROWS-1) MOD NUMROWS\n        CALL DRAWFULLTRACK\n      END IF\n    END IF\n  END IF\n\n  IF FOCUS=10 THEN\n    CALL HANDLEWHEEL(CURTRA,0,NUMTRAC-1,K$,UPD)\n    IF UPD THEN\n      CALL DRAWTRAINFO\n      CALL DRAWFULLTRACK\n    END IF\n  END IF\n  \n  IF CURCOL=0 THEN\n    CALL HANDLEKEYBOARD(1,3,K$,UPD)\n  ELSE\n    CALL HANDLEHEXPICKER(K$,UPD)\n  END IF\n  IF K$<>\"\" AND UPD AND PLAYER_MODE<>3 THEN\n    CURROW=(CURROW+1) MOD NUMROWS\n    CALL DRAWFULLTRACK\n  END IF\n  \n  IF PLAYER_MODE=3 THEN\n    IF PLAYER_ROW<>CURROW OR PLAYER_TRA<>CURTRA THEN\n      T=CURTRA\n      CURROW=PLAYER_ROW\n      CURTRA=PLAYER_TRA\n      IF CURTRA<>T THEN CALL DRAWTRAINFO\n      CALL DRAWFULLTRACK\n    END IF\n  END IF\n\n  WAIT VBL\nLOOP\n\nTRACKMENU:\nMENU$(0)=\"COPY\"\nMENU$(1)=\"PASTE\"\nMENU$(2)=\"OCTAVE UP\"\nMENU$(3)=\"OCTAVE DOWN\"\nMENU$(4)=\"NOTE UP\"\nMENU$(5)=\"NOTE DOWN\"\nMENU$(6)=\"CANCEL\"\nCALL SHOWMENU(\"EDIT TRACK\",6,R)\nIF R=0 THEN COPY ATRAC+CURTRA*TRACSIZE,TRACSIZE TO ACOPYTRAC\nIF R=1 THEN COPY ACOPYTRAC,TRACSIZE TO ATRAC+CURTRA*TRACSIZE\nIF R=2 THEN CALL TRANSPOSE(CURTRA,+12)\nIF R=3 THEN CALL TRANSPOSE(CURTRA,-12)\nIF R=4 THEN CALL TRANSPOSE(CURTRA,+1)\nIF R=5 THEN CALL TRANSPOSE(CURTRA,-1)\nGOTO TRACKEDITOR\n\nSUB UPDATETRACURSOR\n  IF FOCUS>=0 THEN\n    CALL SETCURSOR(ZONEX(FOCUS), ZONEY(FOCUS), ZONEW(FOCUS), ZONEH(FOCUS))\n  ELSE\n    IF CURCOL=0 THEN\n      CALL SETCURSOR(3,8,3,1)\n    ELSE\n      CALL SETCURSOR(7+(CURCOL-1)*2,8,1,1)\n    END IF\n  END IF\nEND SUB\n\nSUB UPDATETRAINPUT\n  IF CURCOL=0 THEN\n    CALL SHOWKEYBOARD\n  ELSE\n    CALL SHOWHEXPICKER\n  END IF\nEND SUB\n\nSUB DRAWTRAINFO\n  PAL 0\n  BG FILL 0,2 TO 19,3 CHAR 1\n  TEXT 0,2,\"TR\"\n  PAL 2\n  NUMBER 0,3,CURTRA,2\n  PAL 0\n  TEXT 3,3,\"N   S V C P\"\nEND SUB\n\nSUB DRAWFULLTRACK\n  PAL 3\n  BG FILL 0,4 TO 20,13 CHAR 0\n  CALL DRAWPOS\n  CALL DRAWTRACK(CURTRA,3)\n  CALL DRAWTRACKPARAMS\nEND SUB\n\nSUB DRAWTRACKPARAMS\n  FOR I=0 TO 9\n    R=I+CURROW-4\n    IF R>=0 AND R<NUMROWS THEN\n      CALL DRAWPARAM(CURTRA,R,4+I)\n    END IF\n  NEXT I\nEND SUB\n\nSUB DRAWPARAM(TRA,R,Y)\n  IF R=CURROW THEN PAL 1 ELSE PAL 3\n  NOTE=0\n  SND=0\n  VOL=0\n  COM=0\n  PAR=0\n  CALL GETROW(TRA,R,NOTE,SND,VOL,COM,PAR)\n  IF NOTE>0 AND NOTE<255 THEN\n    TEXT 7,Y,HEXS$(SND)\n    TEXT 9,Y,HEXS$(VOL)\n  ELSE\n    TEXT 7,Y,\"- -\"\n  END IF\n  IF COM>0 OR PAR>0 THEN\n    TEXT 11,Y,HEXS$(COM)\n    TEXT 13,Y,HEXS$(PAR)\n  ELSE\n    TEXT 11,Y,\"- -\"\n  END IF\nEND SUB\n\n'===================================\n\nSOUNDEDITOR:\n\nSELTAB=2\n\nCALL METERSOFF\nSPRITE OFF\nCALL RESETZONES\n\nBG SOURCE ROM(3)\nBG COPY 0,10,20,2 TO 0,0\nPAL 0\nBG FILL 0,2 TO 19,16 CHAR 1\n\nBG COPY 0,12,20,2 TO 0,2\nTEXT 0,2,\"SND\"\nFOR I=0 TO 15\n  TEXT 4+I,2,HEXS$(I)\nNEXT I\n\nPAL 0\nTEXT 0,5,\"SND:\"\nTEXT 4,5,\"WAV\"\nTEXT 8,5,\"PW\"\nTEXT 12,5,\"LEN\"\n\nTEXT 0,7,\"ENV:\"\nTEXT 4,7,\"ATT\"\nTEXT 8,7,\"DEC\"\nTEXT 12,7,\"SUS\"\nTEXT 16,7,\"REL\"\n\nTEXT 0,9,\"LFO:\"\nTEXT 4,9,\"RAT\"\nTEXT 8,9,\"FRQ\"\nTEXT 12,9,\"VOL\"\nTEXT 16,9,\"PW\"\n\nTEXT 4,11,\"WAV\"\nTEXT 8,11,\"INV\"\nTEXT 12,11,\"ENV\"\nTEXT 16,11,\"TRG\"\n\nZONE_PAL=1\nCALL SETZONE(0,0,0,2,2)\nCALL SETZONE(1,2,0,2,2)\nCALL SETZONE(2,4,0,2,2)\nZONE_PAL=-1\nCALL SETZONE(6,12,0,2,2)\nCALL SETZONE(7,14,0,2,2)\nCALL SETZONE(8,16,0,2,2)\nZONE_PAL=1\nCALL SETZONE(9,18,0,2,2)\n\nZONE_PAL=-1\nCALL SETZONE(10,4,2,16,2)\n\nCALL SETZONE(11,4,5,3,2)\nCALL SETZONE(12,8,5,3,2)\nCALL SETZONE(13,12,5,3,2)\n\nCALL SETZONE(14,4,7,3,2)\nCALL SETZONE(15,8,7,3,2)\nCALL SETZONE(16,12,7,3,2)\nCALL SETZONE(17,16,7,3,2)\n\nCALL SETZONE(18,4,9,3,2)\nCALL SETZONE(19,8,9,3,2)\nCALL SETZONE(30,12,9,3,2)\nCALL SETZONE(31,16,9,3,2)\n\nCALL SETZONE(32,4,11,3,2)\nCALL SETZONE(33,8,11,3,2)\nCALL SETZONE(34,12,11,3,2)\nCALL SETZONE(35,16,11,3,2)\n\nFOCUS=-1\nCALL DRAWSNDSEL\nCALL DRAWSNDPARAMS\nCALL SHOWKEYBOARD\nCALL UPDATESNDCURSOR\n\nOLDX=-1\n\nDO\n  CALL UPDATEZONES\n  \n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=0 THEN\n      STOP\n      CALL STOPPLAYER\n    END IF\n    IF CUR_ZONE=2 THEN GOTO SOUNDMENU\n    IF CUR_ZONE=9 THEN GOTO DISKMENU\n  END IF\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=1 THEN\n      FOCUS=-1\n      CALL SHOWKEYBOARD\n    END IF\n    IF CUR_ZONE=6 THEN GOTO PATTERNEDITOR\n    IF CUR_ZONE=7 THEN GOTO TRACKEDITOR\n  END IF\n\n  IF CUR_ZONE=10 AND (ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG) THEN\n    CURSND=ZONE_IN_X\n    CALL DRAWSNDSEL\n    CALL DRAWSNDPARAMS\n  END IF\n\n  IF ZONE_EVENT=E_DOWN THEN\n    IF (CUR_ZONE>=12 AND CUR_ZONE<=19) OR (CUR_ZONE>=30 AND CUR_ZONE<=31) THEN\n      IF FOCUS<>CUR_ZONE THEN\n        FOCUS=CUR_ZONE\n        IF CUR_ZONE=13 THEN\n          CALL SHOWWHEEL\n        ELSE\n          CALL SHOWHEXPICKER\n        END IF\n      END IF\n    ELSE IF CUR_ZONE=11 OR CUR_ZONE>=32 THEN\n      IF FOCUS>=0 THEN\n        FOCUS=-1\n        CALL SHOWKEYBOARD\n      END IF\n    END IF\n    CALL UPDATESNDCURSOR\n    A=AINST+CURSND*INSTSIZE\n    IF CUR_ZONE=11 THEN\n      'WAVEFORM\n      V=(PEEK(A)\\16) AND %11\n      POKE A,(PEEK(A) AND %11001111) OR (((V+1) MOD 4)*16)\n      CALL DRAWSNDPARAMS\n    END IF\n    IF CUR_ZONE=32 THEN\n      'LFO WAVEFORM\n      V=PEEK(A+4) AND %11\n      POKE A+4,(PEEK(A+4) AND %11111100) OR ((V+1) MOD 4)\n      CALL DRAWSNDPARAMS\n    END IF\n    IF CUR_ZONE=33 THEN\n      'LFO INVERT\n      POKE A+4,PEEK(A+4) XOR %00000100\n      CALL DRAWSNDPARAMS\n    END IF\n    IF CUR_ZONE=34 THEN\n      'LFO ENV MODE\n      POKE A+4,PEEK(A+4) XOR %00001000\n      CALL DRAWSNDPARAMS\n    END IF\n    IF CUR_ZONE=35 THEN\n      'LFO TRIGGER\n      POKE A+4,PEEK(A+4) XOR %00010000\n      CALL DRAWSNDPARAMS\n    END IF\n  END IF\n\n  K$=INKEY$\n  IF K$=\" \" THEN\n    STOP\n    CALL STOPPLAYER\n  END IF\n\n  IF FOCUS=-1 THEN\n    CALL HANDLEKEYBOARD(2,0,K$,UPD)\n  ELSE IF FOCUS=13 THEN\n    'WHEEL: LENGTH\n    IF CUR_ZONE=20 THEN\n      IF ZONE_EVENT=E_DOWN THEN\n        VOLUME CURVOI,15,%11\n        CALL SETSOUND(CURVOI,CURSND)\n        PLAY CURVOI,CURNOTE\n      ELSE IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN\n        STOP CURVOI\n      END IF\n    END IF\n    A=AINST+CURSND*INSTSIZE\n    V=PEEK(A+1)\n    CALL HANDLEWHEEL(V,0,255,K$,UPD)\n    IF UPD THEN\n      POKE A+1,V\n      IF V>0 THEN\n        POKE A,PEEK(A) OR %01000000\n      ELSE\n        POKE A,PEEK(A) AND %10111111\n      END IF\n      CALL DRAWSNDPARAMS\n      CALL SETSOUND(CURVOI,CURSND)\n      PLAY CURVOI,CURNOTE\n    END IF\n  ELSE\n    CALL HANDLESNDHEXPICKER(K$)\n  END IF\n\n  WAIT VBL\nLOOP\n\nSOUNDMENU:\nMENU$(0)=\"COPY\"\nMENU$(1)=\"PASTE\"\nMENU$(2)=\"CANCEL\"\nCALL SHOWMENU(\"EDIT SOUND\",2,R)\nIF R=0 THEN COPY AINST+CURSND*INSTSIZE,INSTSIZE TO ACOPYINST\nIF R=1 THEN COPY ACOPYINST,INSTSIZE TO AINST+CURSND*INSTSIZE\nGOTO SOUNDEDITOR\n\nSUB UPDATESNDCURSOR\n  IF FOCUS>=0 THEN\n    CALL SETCURSOR(ZONEX(FOCUS), ZONEY(FOCUS), ZONEW(FOCUS), ZONEH(FOCUS))\n  ELSE\n    SPRITE OFF\n  END IF\nEND SUB\n\nSUB DRAWSNDSEL\n  BG TINT 4,2 TO 19,3 PAL 0\n  BG TINT 4+CURSND,2 TO 4+CURSND,3 PAL 1\nEND SUB\n\nSUB DRAWSNDPARAMS\n  A=AINST+CURSND*INSTSIZE\n  PAL 2\n  'SND:\n  TEXT 4,6,SNDWAV$(PEEK(A)\\16 MOD 4)\n  TEXT 8,6,HEXS$(PEEK(A) MOD 16)\n  IF PEEK(A) AND %01000000 THEN\n    TEXT 12,6,\"   \"\n    TEXT 12,6,STR$(PEEK(A+1))\n  ELSE\n    TEXT 12,6,\"OFF\"\n  END IF\n  'ENV:\n  TEXT 4,8,HEXS$(PEEK(A+2) MOD 16)\n  TEXT 8,8,HEXS$(PEEK(A+2)\\16)\n  TEXT 12,8,HEXS$(PEEK(A+3) MOD 16)\n  TEXT 16,8,HEXS$(PEEK(A+3)\\16)\n  'LFO:\n  TEXT 4,10,HEXS$(PEEK(A+5) MOD 16)\n  TEXT 8,10,HEXS$(PEEK(A+5)\\16)\n  TEXT 12,10,HEXS$(PEEK(A+6) MOD 16)\n  TEXT 16,10,HEXS$(PEEK(A+6)\\16)\n  'LFO ATTR:\n  TEXT 4,12,LFOWAV$(PEEK(A+4) MOD 4)\n  TEXT 8,12,FLAG$(PEEK(A+4)\\4 MOD 2)\n  TEXT 12,12,FLAG$(PEEK(A+4)\\8 MOD 2)\n  TEXT 16,12,FLAG$(PEEK(A+4)\\16 MOD 2)\nEND SUB\n\nSUB HANDLESNDHEXPICKER(KEY$)\n  KEYVAL=INSTR(VALKEYS$,KEY$)-1\n  IF CUR_ZONE=20 OR KEYVAL>=0 THEN\n    IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG OR KEYVAL>=0 THEN\n      A=AINST+CURSND*INSTSIZE\n      IF KEYVAL>=0 THEN N=KEYVAL ELSE N=ZONE_IN_X\n      'SND:\n      IF FOCUS=12 THEN POKE A,(PEEK(A) AND $F0) OR N\n      'ENV:\n      IF FOCUS=14 THEN POKE A+2,(PEEK(A+2) AND $F0) OR N\n      IF FOCUS=15 THEN POKE A+2,(PEEK(A+2) AND $0F) OR N*16\n      IF FOCUS=16 THEN POKE A+3,(PEEK(A+3) AND $F0) OR N\n      IF FOCUS=17 THEN POKE A+3,(PEEK(A+3) AND $0F) OR N*16\n      'LFO:\n      IF FOCUS=18 THEN POKE A+5,(PEEK(A+5) AND $F0) OR N\n      IF FOCUS=19 THEN POKE A+5,(PEEK(A+5) AND $0F) OR N*16\n      IF FOCUS=30 THEN POKE A+6,(PEEK(A+6) AND $F0) OR N\n      IF FOCUS=31 THEN POKE A+6,(PEEK(A+6) AND $0F) OR N*16\n      CALL DRAWSNDPARAMS\n      VOLUME CURVOI,15,%11\n      CALL SETSOUND(CURVOI,CURSND)\n      PLAY CURVOI,CURNOTE\n    ELSE IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN\n      STOP CURVOI\n    END IF\n  END IF\nEND SUB\n\n'==== INPUT BARS ====\n\nSUB SHOWKEYBOARD\n  BG COPY 0,2,20,2 TO 0,14\n  CALL DRAWOCTAVE\n  CALL CLEARZONES(20,29)\n  ZONE_PAL=-1\n  CALL SETZONE(20,4,14,12,2)\n  ZONE_PAL=1\n  CALL SETZONE(21,0,14,2,2)\n  CALL SETZONE(22,2,14,2,2)\n  CALL SETZONE(23,16,14,2,2)\n  CALL SETZONE(24,18,14,2,2)\nEND SUB\n\n'MODE 0: PATTERN EDITOR\n'MODE 1: TRACK EDITOR\n'MODE 2: SOUND EDITOR\nSUB HANDLEKEYBOARD(MODE,VOIX,KEY$,UPD)\n  UPD=0\n  KEY=INSTR(KEYS$,KEY$)\n  IF FOCUS=-1 THEN\n    IF CUR_ZONE=20 OR KEY>0 THEN\n      IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG OR KEY>0 THEN\n        IF MODE=0 THEN CALL PREPARETRACK(CURTRA,VOIX)\n        IF KEY>0 THEN\n          N=CUROCT*12+KEY\n        ELSE\n          N=CUROCT*12+ZONE_IN_X+1\n        END IF\n        IF N>=1 AND N<=95 THEN\n          CURNOTE=N\n          IF MODE=2 THEN\n            VOLUME CURVOI,15,%11\n            CALL SETSOUND(CURVOI,CURSND)\n            PLAY CURVOI,N\n          ELSE IF CURTRA>=0 THEN\n            CALL SETNOTE(CURTRA,CURROW,N)\n            UPD=-1\n            CALL PLAYROW(CURTRA,CURROW,CURVOI)\n          END IF\n        END IF\n      ELSE IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN\n        STOP CURVOI\n      END IF\n    END IF\n    IF CUR_ZONE=21 AND ZONE_EVENT=E_DOWN THEN\n      IF CUROCT>0 THEN\n        CUROCT=CUROCT-1\n        CALL DRAWOCTAVE\n      END IF\n    END IF\n    IF CUR_ZONE=22 AND ZONE_EVENT=E_DOWN THEN\n      IF CUROCT<7 THEN\n        CUROCT=CUROCT+1\n        CALL DRAWOCTAVE\n      END IF\n    END IF\n    IF MODE<>2 THEN\n      IF (CUR_ZONE=23 AND ZONE_EVENT=E_UP) OR KEY$=CHR$(10) THEN\n        IF MODE=0 THEN CALL PREPARETRACK(CURTRA,VOIX)\n        IF CURTRA>=0 THEN\n          CALL SETNOTE(CURTRA,CURROW,255)\n          UPD=-1\n          STOP CURVOI\n        END IF\n      END IF\n      IF ((CUR_ZONE=24 AND ZONE_EVENT=E_UP) OR KEY$=CHR$(8)) AND CURTRA>=0 THEN\n        CALL SETNOTE(CURTRA,CURROW,0)\n        UPD=-1\n        STOP CURVOI\n      END IF\n      IF UPD THEN\n        CALL DRAWNOTE(CURTRA,CURROW,VOIX,8)\n        IF MODE=1 THEN CALL DRAWPARAM(CURTRA,CURROW,8)\n      END IF\n    END IF\n  END IF\nEND SUB\n\nSUB SHOWWHEEL\n  BG COPY 0,4,20,2 TO 0,14\n  CALL CLEARZONES(20,29)\n  ZONE_PAL=-1\n  CALL SETZONE(20,0,14,20,2)\nEND SUB\n\nSUB HANDLEWHEEL(VALUE,VMIN,VMAX,KEY$,UPD)\n  UPD=0\n  IF CUR_ZONE=20 THEN\n    IF ZONE_EVENT=E_DOWN THEN\n      OLDX=ZONE_IN_X\n    ELSE IF ZONE_EVENT=E_DRAG THEN\n      VALUE=MAX(VMIN,MIN(VMAX,VALUE+ZONE_IN_X-OLDX))\n      UPD=-1\n      OLDX=ZONE_IN_X\n    END IF\n  END IF\n  IF KEY$<>\"\" THEN\n    K=ASC(KEY$)\n    IF K=17 THEN\n      VALUE=MAX(VMIN,MIN(VMAX,VALUE+10))\n      UPD=-1\n    ELSE IF K=18 THEN\n      VALUE=MAX(VMIN,MIN(VMAX,VALUE-10))\n      UPD=-1\n    ELSE IF K=19 THEN\n      VALUE=MAX(VMIN,MIN(VMAX,VALUE-1))\n      UPD=-1\n    ELSE IF K=20 THEN\n      VALUE=MAX(VMIN,MIN(VMAX,VALUE+1))\n      UPD=-1\n    ELSE IF KEY$>=\"0\" AND KEY$<=\"9\" THEN\n      V$=STR$(MAX(0,VALUE))\n      IF VMAX<99 THEN L=1 ELSE L=2\n      V$=RIGHT$(V$,L)+KEY$\n      V=VAL(V$)\n      IF V>VMAX THEN V=VAL(KEY$)\n      VALUE=V\n      UPD=-1\n    ELSE IF KEY$=CHR$(8) THEN\n      VALUE=VMIN\n      UPD=-1\n    END IF\n  END IF\nEND SUB\n\nSUB SHOWHEXPICKER\n  BG COPY 0,6,20,2 TO 0,14\n  PAL 0\n  FOR I=0 TO 15\n    TEXT I,15,HEXS$(I)\n  NEXT I\n  CALL CLEARZONES(20,29)\n  ZONE_PAL=-1\n  CALL SETZONE(20,0,14,16,2)\nEND SUB\n\nSUB HANDLEHEXPICKER(KEY$,UPD)\n  UPD=0\n  KEYVAL=INSTR(VALKEYS$,KEY$)-1\n  IF FOCUS=-1 THEN\n    IF CUR_ZONE=20 OR KEYVAL>=0 THEN\n      IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG OR KEYVAL>=0 THEN\n        IF KEYVAL>=0 THEN N=KEYVAL ELSE N=ZONE_IN_X\n        IF CURCOL=1 THEN\n          CALL SETSND(CURTRA,CURROW,N)\n          CALL PLAYROW(CURTRA,CURROW,CURVOI)\n          CURSND=N\n        END IF\n        IF CURCOL=2 THEN\n          CALL SETVOL(CURTRA,CURROW,N)\n          CALL PLAYROW(CURTRA,CURROW,CURVOI)\n        END IF\n        IF CURCOL=3 THEN CALL SETCOM(CURTRA,CURROW,N)\n        IF CURCOL=4 THEN CALL SETPAR(CURTRA,CURROW,N)\n        CALL DRAWPARAM(CURTRA,CURROW,8)\n        UPD=-1\n      ELSE IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN\n        STOP CURVOI\n      END IF\n    END IF\n  END IF\nEND SUB\n\n'===================================\n\nDISKMENU:\n\nCALL METERSOFF\nSPRITE OFF\nCALL RESETZONES\n\nFILES\nCALL DRAWDISKMENU\n\nZONE_PAL=-1\nCALL SETZONE(0,0,2,18,11)\n\nZONE_PAL=1\nCALL SETZONE(1,18,2,2,2)\nCALL SETZONE(2,18,11,2,2)\nCALL SETZONE(3,0,14,2,2)\nCALL SETZONE(4,16,14,2,2)\nCALL SETZONE(5,18,14,2,2)\nCALL SETZONE(6,14,14,2,2)\n\nDO\n  CALL UPDATEZONES\n  \n  IF ZONE_EVENT=E_DOWN OR ZONE_EVENT=E_DRAG THEN\n    IF CUR_ZONE=0 THEN\n      SOUNDFILE=ZONE_IN_Y+FILESOFFS\n      CALL DRAWFILES\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_DOWN THEN\n    IF CUR_ZONE=1 THEN\n      FILESOFFS=0\n      CALL DRAWFILES\n    END IF\n    IF CUR_ZONE=2 THEN\n      FILESOFFS=5\n      CALL DRAWFILES\n    END IF\n  END IF\n\n  IF ZONE_EVENT=E_UP THEN\n    IF CUR_ZONE=3 THEN GOTO MAIN\n    IF CUR_ZONE=4 THEN\n      OK=0\n      CALL BLOAD(OK)\n      IF OK THEN GOTO MAIN\n    END IF\n    IF CUR_ZONE=5 THEN\n      OK=0\n      CALL BSAVE(OK)\n      IF OK THEN GOTO MAIN\n    END IF\n    IF CUR_ZONE=6 THEN\n      MENU$(0)=\"CLEAR ALL\"\n      MENU$(1)=\"KEEP SOUNDS\"\n      MENU$(2)=\"CANCEL\"\n      CALL SHOWMENU(\"CLEAR MUSIC?\",2,R)\n      CALL DRAWDISKMENU\n      IF R=0 THEN\n        CALL BNEW\n        GOTO MAIN\n      END IF\n      IF R=1 THEN\n        CALL BCLEARTRACKS\n        GOTO MAIN\n      END IF\n    END IF\n  END IF\n\n  WAIT VBL\nLOOP\n\nSUB DRAWDISKMENU\n  BG SOURCE ROM(4)\n  BG COPY 0,0,20,16 TO 0,0\n  PAL 1\n  TEXT 0,0,\"SOUND FILE          \"\n  SPRITE OFF\n  CALL DRAWFILES\nEND SUB\n\nSUB DRAWFILES\n  FOR I=0 TO 10\n    N=I+FILESOFFS\n    IF N=SOUNDFILE THEN PAL 1 ELSE PAL 0\n    NUMBER 0,2+I,N,2\n    TEXT 2,2+I,\":               \"\n    TEXT 3,2+I,LEFT$(FILE$(N),17)\n  NEXT I\nEND SUB\n\nSUB BLOAD(OK)\n  OK=0\n  IF SOUNDFILE=-1 THEN\n    CALL SHOWMESSAGE(\"SELECT FILE\")\n    EXIT SUB\n  END IF\n  CALL CLEARMUSIC\n  LOAD SOUNDFILE,$A000,SONGSIZE\n  CALL INITSTATUS\n  OK=-1\nEND SUB\n\nSUB BSAVE(OK)\n  IF SOUNDFILE=-1 THEN\n    CALL SHOWMESSAGE(\"SELECT FILE\")\n    EXIT SUB\n  END IF\n  LT=-1\n  HP=0\n  CALL GETLASTTRACK(LT)\n  CALL HASPATTERNS(HP)\n  F$=FILE$(SOUNDFILE)\n  IF F$=\"\" THEN F$=\"SOUND\"\n  S=NUMINST*INSTSIZE\n  IF LT>=0 OR HP THEN\n    S=S+NUMPATT*PATTSIZE+(LT+1)*TRACSIZE\n  END IF\n  SAVE SOUNDFILE,F$,$A000,S\n  CALL DRAWFILES\n  CALL SHOWMESSAGE(\"SAVED\")\n  OK=-1\nEND SUB\n\nSUB BNEW\n  CALL CLEARMUSIC\n  CALL INITSTATUS\n  SOUNDFILE=-1\nEND SUB\n\nSUB BCLEARTRACKS\n  CALL CLEARPATTERNS\n  CALL CLEARTRACKS\n  CALL INITSTATUS\n  SOUNDFILE=-1\nEND SUB\n\n'===================================\n\nSUB SETNOTE(TRA,ROW,N)\n  A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE\n  POKE A,N\n  IF N>0 AND N<255 THEN\n    CALL SETSND(TRA,ROW,CURSND)\n    CALL SETVOL(TRA,ROW,15)\n  ELSE IF N=0 THEN\n    POKE A+1,0\n  END IF\nEND SUB\n\nSUB SETSND(TRA,ROW,N)\n  A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE\n  NOTE=PEEK(A)\n  IF NOTE>0 AND NOTE<255 THEN\n    POKE A+1,(PEEK(A+1) AND $0F) OR N*16\n  END IF\nEND SUB\n\nSUB SETVOL(TRA,ROW,N)\n  A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE\n  NOTE=PEEK(A)\n  IF NOTE>0 AND NOTE<255 THEN\n    POKE A+1,(PEEK(A+1) AND $F0) OR N\n  END IF\nEND SUB\n\nSUB SETCOM(TRA,ROW,N)\n  A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE+2\n  POKE A,(PEEK(A) AND $0F) OR N*16\nEND SUB\n\nSUB SETPAR(TRA,ROW,N)\n  A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE+2\n  POKE A,(PEEK(A) AND $F0) OR N\nEND SUB\n\nSUB GETROW(TRA,ROW,NOTE,SND,VOL,COM,PAR)\n  A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE\n  NOTE=PEEK(A)\n  SND=PEEK(A+1)\\16\n  VOL=PEEK(A+1) AND $0F\n  COM=PEEK(A+2)\\16\n  PAR=PEEK(A+2) AND $0F\nEND SUB\n\nSUB TRANSPOSE(TRA,N)\n  FOR ROW=0 TO NUMROWS-1\n    A=ATRAC+TRA*TRACSIZE+ROW*ROWSIZE\n    NOTE=PEEK(A)\n    IF NOTE>0 AND NOTE<255 THEN\n      TN=NOTE+N\n      IF TN>0 AND TN<255 THEN POKE A,TN\n    END IF\n  NEXT ROW\nEND SUB\n\nSUB SETTRACK(PAT,VOI,TRA)\n  A=APATT+PAT*PATTSIZE+VOI\n  HI=PEEK(A) AND %10000000\n  IF TRA>=0 THEN POKE A,(TRA OR HI) ELSE POKE A,(%01000000 OR HI)\nEND SUB\n\nSUB GETTRACK(PAT,VOI,TRA)\n  A=APATT+PAT*PATTSIZE+VOI\n  TRA=(PEEK(A) AND %01111111)\n  IF TRA=%01000000 THEN TRA=-1\nEND SUB\n\nSUB ISPATTERNEMPTY(PAT,EMP)\n  EMP=-1\n  T=-1\n  FOR V=0 TO 3\n    CALL GETTRACK(PAT,V,T)\n    IF T>=0 THEN\n      EMP=0\n      EXIT SUB\n    END IF\n  NEXT V\nEND SUB\n\nSUB HASPATTERNS(HAS)\n  HAS=0\n  EMP=0\n  FOR P=0 TO NUMPATT-1\n    CALL ISPATTERNEMPTY(P,EMP)\n    IF NOT EMP THEN\n      HAS=-1\n      EXIT SUB\n    END IF\n  NEXT P\nEND SUB\n\nSUB GETFREETRACK(TRA)\n  NOTE=0\n  SND=0\n  VOL=0\n  COM=0\n  PAR=0\n  FOR T=0 TO NUMTRAC-1\n    OK=-1\n    FOR R=0 TO NUMROWS-1\n      CALL GETROW(T,R,NOTE,SND,VOL,COM,PAR)\n      IF NOTE>0 OR SND>0 OR VOL>0 OR COM>0 OR PAR>0 THEN\n        OK=0\n        R=31\n      END IF\n    NEXT R\n    IF OK THEN\n      TRA=T\n      EXIT SUB\n    END IF\n  NEXT T\n  TRA=-1\nEND SUB\n\nSUB GETLOOPSTART(PAT)\n  V=0\n  FOR P=PAT TO 0 STEP -1\n    CALL GETLOOP(P,0,V)\n    IF V=1 THEN\n      PAT=P\n      EXIT SUB\n    END IF\n  NEXT P\n  PAT=0\nEND SUB\n\nSUB SETLOOP(PAT,PARAM,VALUE)\n  A=APATT+PAT*PATTSIZE+PARAM\n  TRA=(PEEK(A) AND %01111111)\n  POKE A,(VALUE*128 OR TRA)\nEND SUB\n\nSUB GETLOOP(PAT,PARAM,VALUE)\n  A=APATT+PAT*PATTSIZE+PARAM\n  VALUE=PEEK(A)\\128\nEND SUB\n\nSUB CLEARMUSIC\n  'SOUNDS\n  CALL CLEARSOUNDS\n  'PATTERNS\n  CALL CLEARPATTERNS\n  'TRACKS\n  CALL CLEARTRACKS\nEND SUB\n\nSUB CLEARPATTERNS\n  FILL APATT,NUMPATT*PATTSIZE,%01000000\nEND SUB\n\nSUB CLEARTRACKS\n  FOR T=0 TO NUMTRAC-1\n    CALL CLEARTRACK(T)\n  NEXT T\nEND SUB\n\nSUB CLEARSOUNDS\n  COPY ROM(15),NUMINST*INSTSIZE TO AINST\nEND SUB\n\nSUB CLEARTRACK(TRA)\n  A=ATRAC+TRA*TRACSIZE\n  FILL A,TRACSIZE,0\nEND SUB\n\nSUB GETLASTTRACK(TRA)\n  NOTE=0\n  SND=0\n  VOL=0\n  COM=0\n  PAR=0\n  FOR T=NUMTRAC-1 TO 0 STEP -1\n    OK=0\n    FOR R=0 TO NUMROWS-1\n      CALL GETROW(T,R,NOTE,SND,VOL,COM,PAR)\n      IF NOTE>0 OR SND>0 OR VOL>0 OR COM>0 OR PAR>0 THEN\n        OK=-1\n        R=31\n      END IF\n    NEXT R\n    IF OK THEN\n      TRA=T\n      EXIT SUB\n    END IF\n  NEXT T\n  TRA=-1\nEND SUB\n\n'==== PLAYER ====\n\n'MODE 1: SONG\n'MODE 2: PATTERN\nSUB PLAYMUSIC(PAT,MODE)\n  STOP\n  PLAYER_PAT=PAT\n  PLAYER_MODE=MODE\n  PLAYER_SPEED=8\n  PLAYER_TICK=-1\n  PLAYER_ROW=0\n  PLAYER_BREAK=0\n  PLAYER_TRA=-1\n  PLAYER_VOI=-1\n  SYSTEM 0,0\nEND SUB\n\nSUB PLAYTRACK(TRA,VOI)\n  STOP\n  PLAYER_TRA=TRA\n  PLAYER_VOI=VOI\n  PLAYER_MODE=3\n  PLAYER_SPEED=8\n  PLAYER_TICK=-1\n  PLAYER_ROW=0\n  PLAYER_BREAK=0\n  PLAYER_PAT=-1\n  SYSTEM 0,0\nEND SUB\n\nSUB CHANGEMUSIC(PAT)\n  IF PLAYER_MODE<1 OR PLAYER_MODE>2 THEN EXIT SUB\n  STOP\n  PLAYER_PAT=PAT\n  PLAYER_TICK=-1\n  PLAYER_ROW=0\nEND SUB\n\nSUB CHANGETRACK(TRA)\n  IF PLAYER_MODE<>3 THEN EXIT SUB\n  STOP\n  PLAYER_TRA=TRA\n  PLAYER_TICK=-1\n  PLAYER_ROW=0\nEND SUB\n\nSUB STOPPLAYER\n  PLAYER_MODE=0\n  SYSTEM 0,1\nEND SUB\n\nSUB UPDATEPLAYER\n  CALL UPDMETERSVBL\n  IF PLAYER_MODE=0 THEN EXIT SUB\n  IF PLAYER_TICK=-1 THEN\n    PLAYER_TICK=0\n  ELSE IF PLAYER_TICK=0 THEN\n    IF PLAYER_BREAK THEN\n      PLAYER_BREAK=0\n      PLAYER_ROW=0\n    ELSE\n      PLAYER_ROW=(PLAYER_ROW+1) MOD NUMROWS\n    END IF\n    IF PLAYER_ROW=0 AND PLAYER_MODE=1 THEN\n      V=0\n      CALL GETLOOP(PLAYER_PAT,2,V)\n      IF V=1 THEN\n        PLAYER_MODE=0\n        EXIT SUB\n      END IF\n      CALL GETLOOP(PLAYER_PAT,1,V)\n      IF V=1 THEN\n        CALL GETLOOPSTART(PLAYER_PAT)\n      ELSE\n        P=PLAYER_PAT+1\n        IF P<NUMPATT THEN\n          E=0\n          CALL ISPATTERNEMPTY(P,E)\n          IF E THEN\n            PLAYER_MODE=0\n            EXIT SUB\n          ELSE\n            PLAYER_PAT=P\n          END IF\n        ELSE\n          PLAYER_MODE=0\n          EXIT SUB\n        END IF\n      END IF\n    END IF\n  END IF\n  IF PLAYER_TICK=0 THEN\n    IF PLAYER_MODE=3 THEN\n      CALL PLAYROW(PLAYER_TRA,PLAYER_ROW,PLAYER_VOI)\n    ELSE\n      T=-1\n      FOR V=0 TO 3\n        CALL GETTRACK(PLAYER_PAT,V,T)\n        IF T>=0 THEN CALL PLAYROW(T,PLAYER_ROW,V)\n      NEXT V\n    END IF\n    IF PLAYER_SPEED=0 THEN\n      PLAYER_MODE=0\n      EXIT SUB\n    END IF\n  END IF\n  PLAYER_TICK=(PLAYER_TICK+1) MOD PLAYER_SPEED\nEND SUB\n\nSUB PLAYROW(TRA,ROW,VOI)\n  NOTE=0\n  SND=0\n  VOL=0\n  COM=0\n  PAR=0\n  CALL GETROW(TRA,ROW,NOTE,SND,VOL,COM,PAR)\n  IF NOTE>0 AND NOTE<255 THEN CALL SETSOUND(VOI,SND)\n  IF VOL>0 THEN VOLUME VOI,VOL,\n  CALL COMMAND(VOI,COM,PAR)\n  IF NOTE=255 THEN\n    STOP VOI\n  ELSE IF NOTE>0 THEN\n    PLAY VOI,NOTE\n  END IF\nEND SUB\n\nSUB SETSOUND(VOI,SND)\n  A=AINST+SND*INSTSIZE\n  TA=$FF40+VOI*12+4\n  COPY A,INSTSIZE TO TA\nEND SUB\n\nSUB COMMAND(VOI,COM,PAR)\n  IF COM=0 AND PAR=0 THEN EXIT SUB\n  IF COM=$0 THEN\n    VOLUME VOI,,PAR AND %11\n  ELSE IF COM=$1 THEN\n    ENVELOPE VOI,PAR,,,\n  ELSE IF COM=$2 THEN\n    ENVELOPE VOI,,PAR,,\n  ELSE IF COM=$3 THEN\n    ENVELOPE VOI,,,PAR,\n  ELSE IF COM=$4 THEN\n    ENVELOPE VOI,,,,PAR\n  ELSE IF COM=$5 THEN\n    LFO VOI,PAR,,,\n  ELSE IF COM=$6 THEN\n    LFO VOI,,PAR,,\n  ELSE IF COM=$7 THEN\n    LFO VOI,,,PAR,\n  ELSE IF COM=$8 THEN\n    LFO VOI,,,,PAR\n  ELSE IF COM=$9 THEN\n    SOUND VOI,,PAR,\n  ELSE IF COM=$D THEN\n    PLAYER_SPEED=$10+PAR\n  ELSE IF COM=$E THEN\n    PLAYER_SPEED=PAR\n  ELSE IF COM=$F THEN\n    IF PAR=0 THEN\n      PLAYER_BREAK=-1\n    ELSE IF PAR=1 THEN\n      STOP VOI\n      VOLUME VOI,0,\n    END IF\n  END IF\nEND SUB\n\n'==== GENERIC UI ====\n\nSUB SHOWMESSAGE(MSG$)\n  COPY $9000,40 TO $D000\n  PAL 1\n  BG FILL 0,0 TO 19,0 CHAR 192\n  TEXT 0,0,MSG$\n  WAIT 60\n  COPY $D000,40 TO $9000\nEND SUB\n\nSUB SHOWMENU(MSG$,MAXI,RESULT)\n  PAL 1\n  PRIO 1\n  BG FILL 0,0 TO 19,0 CHAR 192\n  TEXT (20-LEN(MSG$))/2,0,MSG$\n  PAL 0\n  Y=3+MAXI*2\n  BG FILL 0,1 TO 19,Y CHAR 1\n  BG FILL 0,1 TO 19,1 CHAR 31\n  BG FILL 0,Y TO 19,Y CHAR 16\n  FOR I=0 TO MAXI\n    T$=MENU$(I)\n    TEXT (20-LEN(T$))/2,2+I*2,T$\n  NEXT I\n  WHILE TOUCH\n    WAIT VBL\n  WEND\n  DO\n    IF TOUCH AND TOUCH.Y>=12 THEN\n      I=(((TOUCH.Y-4)\\8)-1)\\2\n      IF I<=MAXI THEN\n        CALL HIGHLIGHT(0,2+I*2,19,2+I*2)\n        WHILE TOUCH\n          WAIT VBL\n        WEND\n        RESULT=I\n        GOTO EXITMENU\n      END IF\n    END IF\n    WAIT VBL\n  LOOP\n  EXITMENU:\n  PRIO 0\nEND SUB\n\nSUB HIGHLIGHT(X1,Y1,X2,Y2)\n  BG TINT X1,Y1 TO X2,Y2 PAL 1\nEND SUB\n\nSUB METERSON\n  SPRITE OFF 60 TO 63\n  FOR V=0 TO 3\n    SPRITE 60+V PAL 4 SIZE 3\n  NEXT V\n  METERSENABLED=-1\n  ON RASTER CALL UPDMETERSRASTER\nEND SUB\n\nSUB METERSOFF\n  METERSENABLED=0\n  ON RASTER OFF\n  SPRITE OFF 60 TO 63\nEND SUB\n\nSUB UPDMETERSVBL\n  IF NOT METERSENABLED THEN EXIT SUB\n  IF SELTAB=0 THEN\n    FOR V=0 TO 3\n      PEAK=PEEK($FF40+V*12+3)\n      SPRITE 60+V,(6+V*4)*8,(255-PEAK)/8+32,128\n    NEXT V\n  ELSE IF SELTAB=1 THEN\n    PEAK=PEEK($FF40+CURVOI*12+3)\n    SPRITE 60,6*8,(255-PEAK)/8+32,128\n  END IF\nEND SUB\n\nSUB UPDMETERSRASTER\n  IF RASTER=64 THEN SPRITE OFF 60 TO 63\nEND SUB\n\n'==== TOUCH ZONES SUBPROGRAMS ====\n\nSUB SETZONE(N,X,Y,W,H)\n  ZONEX(N)=X\n  ZONEY(N)=Y\n  ZONEW(N)=W\n  ZONEH(N)=H\n  IF ZONE_PAL>=0 AND ZONE_PAL<8 THEN\n    'HIGHLIGHT AND NORMAL PALETTE\n    P=CELL.A(X,Y) AND %111\n    ZONEP(N)=(ZONE_PAL*16)+P\n  ELSE\n    'NO HIGHLIGHT\n    ZONEP(N)=$FF\n  END IF\nEND SUB\n\nSUB CLEARZONE(N)\n  CALL SETZONE(N,0,0,0,0)\nEND SUB\n\nSUB CLEARZONES(N,M)\n  FOR I=N TO M\n    CALL SETZONE(I,0,0,0,0)\n  NEXT I\nEND SUB\n\nSUB RESETZONES\n  FOR I=0 TO MAX_ZONE\n    CALL CLEARZONE(I)\n  NEXT I\n  CUR_ZONE=-1\n  ZONE_PAL=-1\nEND SUB\n\nSUB UPDATEZONES\n  CX=TOUCH.X\\8\n  CY=TOUCH.Y\\8\n  IF ZONE_EVENT=E_UP OR ZONE_EVENT=E_OUT THEN CUR_ZONE=-1\n  ZONE_EVENT=0\n  IF CUR_ZONE>=0 THEN\n    CALL INSIDEZONE(CUR_ZONE,CX,CY)\n    IF ZONE_RESULT THEN\n      ZONE_IN_X=CX-ZONEX(CUR_ZONE)\n      ZONE_IN_Y=CY-ZONEY(CUR_ZONE)\n      IF NOT TOUCH THEN\n        CALL PAINTZONE(CUR_ZONE,0)\n        ZONE_EVENT=E_UP\n      ELSE IF CX<>ZONE_LAST_X OR CY<>ZONE_LAST_Y THEN\n        ZONE_EVENT=E_DRAG\n      END IF\n    ELSE\n      CALL PAINTZONE(CUR_ZONE,0)\n      ZONE_EVENT=E_OUT\n    END IF\n  ELSE IF TAP THEN\n    FOR I=0 TO MAX_ZONE\n      IF ZONEW(I)>0 THEN\n        CALL INSIDEZONE(I,CX,CY)\n        IF ZONE_RESULT THEN\n          ZONE_EVENT=E_DOWN\n          CUR_ZONE=I\n          ZONE_IN_X=CX-ZONEX(I)\n          ZONE_IN_Y=CY-ZONEY(I)\n          CALL PAINTZONE(I,1)\n        END IF\n      END IF\n    NEXT I\n  END IF\n  ZONE_LAST_X=CX\n  ZONE_LAST_Y=CY\nEND SUB\n\nSUB INSIDEZONE(N,CX,CY)\n  ZONE_RESULT=(CX>=ZONEX(N) AND CX<ZONEX(N)+ZONEW(N) AND CY>=ZONEY(N) AND CY<ZONEY(N)+ZONEH(N))\nEND SUB\n\nSUB PAINTZONE(N,SEL)\n  P=ZONEP(N)\n  IF P<>$FF THEN\n    IF SEL THEN P=P\\16 ELSE P=P MOD 16\n    BG TINT ZONEX(N),ZONEY(N) TO ZONEX(N)+ZONEW(N)-1,ZONEY(N)+ZONEH(N)-1 PAL P\n  END IF\nEND SUB\n\n'========\n\n#1:MAIN PALETTES\n003F2A15000F050000052A00000A0030\n003F3C38003F2A15003F2A15003F2A15\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n0000000000000000FFFFFFFFFFFFFFFF\nFF809BB28AB28080007F7F7F7F7F7F7F\nFE0119A1A999010101FFFFFFFFFFFFFF\nFF80B1AAB3A28080007F7F7F7F7F7F7F\nFE0139919191010101FFFFFFFFFFFFFF\nFF80808080818387007F7F7F7F7F7F7F\nFE0101010181C1E101FFFFFFFFFFFFFF\n01452911294501FFFFFFFFFFFFFFFFFF\n014931FD314901FFFFFFFFFFFFFFFFFF\nFF80BAA2B2A28080007F7F7F7F7F7F7F\nFE019D9991DD010101FFFFFFFFFFFFFF\n808898BE9888807F7F7F7F7F7F7F7FFF\n80888CBE8C88807F7F7F7F7F7F7F7FFF\n80808080808080807F7F7F7F7F7F7F7F\n0101010101010101FFFFFFFFFFFFFFFF\n00000000000000FFFFFFFFFFFFFFFFFF\n808080808080807F7F7F7F7F7F7F7FFF\nFDFDFDFDFDFD01FFFFFFFFFFFFFFFFFF\nC1F1FDFDF1C101FFFFFFFFFFFFFFFFFF\nFF809BB189B18080007F7F7F7F7F7F7F\nFE0191292911010101FFFFFFFFFFFFFF\n8F9F80808080807F7F7F7F7F7F7F7FFF\nF1F90101010101FFFFFFFFFFFFFFFFFF\nFF8091AAAA918080007F7F7F7F7F7F7F\nFE01B9111191010101FFFFFFFFFFFFFF\nFF80A1A2A2B98080007F7F7F7F7F7F7F\nFE0111A9B929010101FFFFFFFFFFFFFF\nFF80B1B2ABB28080007F7F7F7F7F7F7F\nFE0119A1A199010101FFFFFFFFFFFFFF\nFF000000000000FF00FFFFFFFFFFFFFF\nFF0000000000000000FFFFFFFFFFFFFF\nFF80808080808080FF80808080808080\nFEFEFEFEFEFEFEFE0101010101010101\nBEFEFEFEFEBEBEBEFEBEBEBEBEFEFEFE\nFFAA5555555555550055FFFFFFFFFFFF\n012161FD612101FFFFFFFFFFFFFFFFFF\n011119FD191101FFFFFFFFFFFFFFFFFF\nFF80808080809F8F007F7F7F7F7F7F7F\nFE0101010101F9F101FFFFFFFFFFFFFF\nFEFEFEE6DEDEE6FE0101010101010101\nFDCDCDFDFD7D01FFFFB7B787B7FFFFFF\nFF8099B28BB28080007F7F7F7F7F7F7F\nFE0129A9A991010101FFFFFFFFFFFFFF\nFFAA557560FF60750055FFDF9F009FDF\nFFAA555507FF07550055FFFBF900F9FB\n00003044463F0604FFFFCFBBB9C0F9FB\n00003C3C3C3C0000FFFFC3C3C3C3FFFF\nFE81818181818181017F7F7F7F7F7F7F\nFEFEFEFEFEFE807F0101010101017FFF\nFEFEFEFEFEFE8000FEFEFEFEFEFE8000\n555555555555FFFFFFFFFFFFFFFFFFFF\nFF80B3ABAAB38080007F7F7F7F7F7F7F\nFE01A12121B9010101FFFFFFFFFFFFFF\n878381808080807F7F7F7F7F7F7F7FFF\nE1C18101010101FFFFFFFFFFFFFFFFFF\nEED6D6D6EEFE807F0101010101017FFF\nEECEEEEEC6FE807F0101010101017FFF\nCEF6EEDEC6FE807F0101010101017FFF\nCEF6EEF6CEFE807F0101010101017FFF\nDED6C6F6F6FE807F0101010101017FFF\nC6DECEF6CEFE807F0101010101017FFF\nE6DECED6EEFE807F0101010101017FFF\nC6F6EEEEEEFE807F0101010101017FFF\nFF80BB9293928080007F7F7F7F7F7F7F\nFE0111A939A9010101FFFFFFFFFFFFFF\n3F40808080808080C0BF7F7F7F7F7F7F\nFC0201010101010103FFFFFFFFFFFFFF\n000000003F408080FFFFFFFFC0BF7F7F\n00000000FC020101FFFFFFFF03FFFFFF\n01010101010101FFFFFFFFFFFFFFFFFF\n017D7D7D7D7D01FFFFABABAB8383FFFF\nFF80B3ABAAAB8080007F7F7F7F7F7F7F\nFE01A92939B9010101FFFFFFFFFFFFFF\nFF80BBB2A2BB8080007F7F7F7F7F7F7F\nFE0101818101010101FFFFFFFFFFFFFF\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n81A1E9FDE9A181FFFFFFFFFFFFFFFFFF\n011D011D011D01FFFFFFFFFFFFFFFFFF\n80808080808080FF7F7F7F7F7F7F7FFF\n016D016D016D01FFFFFFFFFFFFFFFFFF\nFF80808080808080007F7F7F7F7F7F7F\n818181818181817F7F7F7F7F7F7F7FFF\nFF80ABB3AAAB8080007F7F7F7F7F7F7F\nFE01A9291191010101FFFFFFFFFFFFFF\n0101017D391101FFFFFFFFFFFFFFFFFF\n1D3D7D7D7D7D01FFFFE7C7C7C7FFFFFF\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n24242424242424241C1C1C1C1C1C1C1C\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n24242424242424241C1C1C1C1C1C1C1C\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n24242424242424241C1C1C1C1C1C1C1C\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n24242424242424241C1C1C1C1C1C1C1C\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0000000000000000FFFFFFFFFFFFFFFF\n0018181818001800FFE7E7E7E7FFE7FF\n006C6C2400000000FF9393DBFFFFFFFF\n00247E24247E2400FFDB81DBDB81DBFF\n00083E380E3E0800FFF7C1C7F1C1F7FF\n0062640810264600FF9D9BF7EFD9B9FF\n001C34386E643A00FFE3CBC7919BC5FF\n0018183000000000FFE7E7CFFFFFFFFF\n000C183030180C00FFF3E7CFCFE7F3FF\n0030180C0C183000FFCFE7F3F3E7CFFF\n000024187E182400FFFFDBE781E7DBFF\n000018187E181800FFFFE7E781E7E7FF\n0000000018183000FFFFFFFFE7E7CFFF\n000000007E000000FFFFFFFF81FFFFFF\n0000000000181800FFFFFFFFFFE7E7FF\n00060C1830604000FFF9F3E7CF9FBFFF\n003C666E76663C00FFC399918999C3FF\n0018381818187E00FFE7C7E7E7E781FF\n003C660C18307E00FFC399F3E7CF81FF\n003C660C06663C00FFC399F3F999C3FF\n0066667E06060600FF999981F9F9F9FF\n007E607C06067C00FF819F83F9F983FF\n001C307C66663C00FFE3CF839999C3FF\n007E060C18303000FF81F9F3E7CFCFFF\n003C663C66663C00FFC399C39999C3FF\n003C663E06663C00FFC399C1F999C3FF\n0000001800180000FFFFFFE7FFE7FFFF\n0000001800183000FFFFFFE7FFE7CFFF\n00000C1830180C00FFFFF3E7CFE7F3FF\n0000007E007E0000FFFFFF81FF81FFFF\n000030180C183000FFFFCFE7F3E7CFFF\n003C660C18001800FFC399F3E7FFE7FF\n003C666E6E603C00FFC39991919FC3FF\n00183C667E666600FFE7C399819999FF\n007C667C66667C00FF839983999983FF\n003C666060663C00FFC3999F9F99C3FF\n00786C66666C7800FF879399999387FF\n007E607860607E00FF819F879F9F81FF\n007E607860606000FF819F879F9F9FFF\n003C606E66663C00FFC39F919999C3FF\n0066667E66666600FF999981999999FF\n003C181818183C00FFC3E7E7E7E7C3FF\n001E060606663C00FFE1F9F9F999C3FF\n00666C78786C6600FF999387879399FF\n0060606060607E00FF9F9F9F9F9F81FF\n0042667E7E666600FFBD9981819999FF\n0066767E6E666600FF998981919999FF\n003C666666663C00FFC399999999C3FF\n007C667C60606000FF8399839F9F9FFF\n003C66666A6C3E00FFC399999593C1FF\n007C667C786C6600FF839983879399FF\n003E603C06067C00FFC19FC3F9F983FF\n007E181818181800FF81E7E7E7E7E7FF\n0066666666663C00FF9999999999C3FF\n00666666663C1800FF99999999C3E7FF\n0066667E7E664200FF9999818199BDFF\n00663C183C666600FF99C3E7C39999FF\n0066663C18181800FF9999C3E7E7E7FF\n007E0C1830607E00FF81F3E7CF9F81FF\n003C303030303C00FFC3CFCFCFCFC3FF\n006030180C060200FF9FCFE7F3F9FDFF\n003C0C0C0C0C3C00FFC3F3F3F3F3C3FF\n00183C6600000000FFE7C399FFFFFFFF\n0000000000007E00FFFFFFFFFFFF81FF\n\n#3:MAIN BG\n00001410020003000400050014001500\n04000500040005004A004B0042004300\n44004500440045000A000B0011001300\n11001300110012001100240011002500\n11005800520053005200510052005000\n11002900180019001800190028002200\n21002200210021002200210022002100\n22002100140015003400350011002400\n11002500380032003100320031003100\n32003100320031003200310011000900\n11000800230023002300230023002300\n2300230023002C002D00230023002300\n23002300230023002300230033003300\n33003300330033003300330033003300\n33003300330033003300330033003300\n33003300300030003000300030003000\n30003000300030003000300030003000\n3000300054001F001F001F0001000100\n01000100010001000100010001000100\n0100010001000100010001000E000100\n01000100400041001400150040004100\n400041004A004B000100010044004500\n42004300440045000A000B0011001300\n11001200110024001100250011005800\n10001000520053005200510052005000\n1100290014001500560057004A004B00\n01000100010001000100010044004500\n44004500420043000A000B0011001200\n11004700110058001000100010001000\n10001000520053005200510052005000\n110029000100010001000F0001000100\n01000100010001000100010001000100\n01000100010001000100010010001000\n10004600550055005500550055005500\n55005500550055005500550055005500\n55005500000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000\n\n#4:DISK BG\n00001410000000000000000000000000\n00000000000000000000000000000000\n0000000000000000000000001E001E00\n1E001E001E001E001E001E001E001E00\n1E001E001E001E001E001E001E001E00\n1E001E00000000000000000000000000\n00000000000000000000000000000000\n00000000000000000600070000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n16001700000000000000000000000000\n00000000000000000000000000000000\n00000000000000000E00010000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0E000100000000000000000000000000\n00000000000000000000000000000000\n00000000000000000E00010000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0E000100000000000000000000000000\n00000000000000000000000000000000\n00000000000000000E00010000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0E000100000000000000000000000000\n00000000000000000000000000000000\n00000000000000000E00010000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n26002700000000000000000000000000\n00000000000000000000000000000000\n0000000000000000360037001E001E00\n1E001E001E001E001E001E001E001E00\n1E001E001E001E001E001E001E001E00\n1E001E001C001D001F001F001F001F00\n1F001F001F001F001F001F001F001F00\n480049001A001B002A002B0011002400\n01000100010001000100010001000100\n0100010001000100110059000C002900\n0D002900\n\n#15:SOUND PRESETS\n2800303A000000001800846C003A0000\n08006060000000002800303019FE0000\n38002020000000003800505000000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n\n"
  },
  {
    "path": "programs/Star Scroller 1.2.nx",
    "content": "'TITLE:   STAR SCROLLER\n'AUTHORS:  TIMO KLOSS, DESBYC\n\nDIM GLOBAL STARX(127)\n\nFOR I=0 TO 127\n  STARX(I)=INT(RND*256)\nNEXT I\nBG 1\nBG FILL 0,0 TO 0,15 CHAR 3\n\nGLOBAL SX,T\n\nON RASTER CALL RASTERFX\n\nT$=\"WELCOME TO LOWRES NX! A VIRTUAL RETRO GAME CONSOLE,WHICH CAN BE PROGRAMMED IN THE CLASSIC BASIC LANGUAGE. HAVE FUN! ... STAR SCROLLER CODE AND GFX BY TIMO, MUSIC BY DESBYC. GREETINGS TO ALL LOWRES NX CODERS!                     \"\n\nCELL SIZE 0,1\nBG 0\nPAL 1\nPRIO 1\nP=1\nL=LEN(T$)\n\nMUSIC\n\nDO\n  IF SX MOD 16=0 THEN\n    P$=MID$(T$,P,1)\n    IF P$>=\"A\" AND P$<=\"Z\" THEN\n      N=ASC(P$)-ASC(\"A\")\n      C=(N\\8)*32+(N MOD 8)*2+64\n    ELSE\n      IF P$=\".\" THEN C=164\n      IF P$=\",\" THEN C=166\n      IF P$=\"!\" THEN C=168\n      IF P$=\"?\" THEN C=170\n      IF P$=\" \" THEN C=174\n    END IF\n    CELL SX/16+10,0,C\n    P=(P MOD L)+1\n  END IF\n \n  SCROLL 0,SX,SIN(T*0.02)*32-56\n \n  P2=PEEK($FF5B)\n \n  FOR I=0 TO 31\n    X1=SIN(T*0.02+I*PI/16)\n    Y1=COS(T*0.03+I*PI/16)\n    X2=COS(T*0.045+I*0.09)\n    Y2=SIN(T*0.035+I*0.1)\n    X3=SIN(T*0.025+I*0.12)\n    Y3=COS(T*0.015+I*0.13)\n  \n    Z=1+P2/1536\n    X=(X1*X2*60+X3*32)*Z\n    Y=(Y1*Y2*60+Y3*32)*Z\n    SPRITE I PAL 2+(I/4) MOD 2\n    SPRITE I,X+76,Y+60,2\n  NEXT I\n \n  T=T+1\n  SX=SX+1\n  WAIT VBL\nLOOP\n\nSUB RASTERFX\n  F=0.5\n  D=((RASTER MOD 4)+1)\n  SCROLL 1,TIMER*F/D+STARX(RASTER),0\nEND SUB\n\n\n#1:MAIN PALETTES\n001B070205383420002F0A05003B2211\n00000000000000000000000000000000\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n00000000000000000000000000000000\n2402B1310183463C3C7ECFCFFFFF7E3C\n80008080800080800080808000808080\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0F3F7046C9909090000F3F3970606060\n00C0E020309090900000C0C0E0606060\nFFFF809F90909F9F007F7F606060607F\n00C0E020B09020400000C0C06060C080\n0F3F7046C9909090000F3F3970606060\n00C0E02030F000000000C0C0E0000000\nFFFF809E91909090007F7F6160606060\n00C0E020309090900000C0C0E0606060\nFFFF809F90909F9F007F7F606060607E\nF0F010F00000000000E0E00000000000\nFFFF809F90909F9F007F7F606060607E\nF0F010F00000000000E0E00000000000\n0F3F7046C9909093000F3F3970606060\n00C0E02030F000F00000C0C0E0000000\nF0F0909090909F9F006060606060607F\nF0F090909090909000606060606060E0\n9F9F809F909090F0607F7F6060606000\n90901090909090F060E0E06060606000\n9F9090909F9F80FF60606060607F7F00\n20909090A020C000C0606060C0C00000\n909090994F46300F60606070393F0F00\n0000F0F02020C000000000E0C0C00000\n909090919F9E80FF60606060617F7F00\n909090902020C000606060E0C0C00000\n819F90909F9F80FF7E606060607F7F00\n00000000F0F010F00000000000E0E000\n819F9090909090F07E60606060606000\n00000000000000000000000000000000\n939390994F46300F61606070393F0F00\nF09090902020C000E06060E0C0C00000\n809F9090909090F07F60606060606000\n10909090909090F0E060606060606000\n3F3F203909090909001F1F0606060606\nC0C040C0000000000080800000000000\nFFFF80FF00000000007F7F0000000000\nF0F010909090909000E0E06060606060\nF0F0909193969C99006060606163677E\nF0F0909020408000006060E0C0800000\nF0F09090909090900060606060606060\n00000000000000000000000000000000\nE0F0998F86909996006070797F6F6660\n70F09010109090900060E0E0E0606060\nF0F88C86939994920070787C6E676361\nF0F0909090909010006060606060E0E0\n0F3F7046C9909090000F3F3970606060\n00C0E020309090900000C0C0E0606060\nFFFF809F90909F9F007F7F606060607F\n00C0E020B090A0200000C0C06060C0C0\n090909093939203F06060606061F1F00\n00000000C0C040C00000000000808000\n00000001FFFE80FF00000000017F7F00\n909090902020C000606060E0C0C00000\n81999492919090F07E67636160606000\n0080C060309090F0000080C0E0606000\n909090909F9F80FF60606060607F7F00\n00000000F0F010F00000000000E0E000\n90909090909090F06060606060606000\n90909090909090F06060606060606000\n91909090909090F06060606060606000\n10909090909090F0E060606060606000\n909090994F46300F60606070393F0F00\n909090902020C000606060E0C0C00000\n809F9090909090F07F60606060606000\nC0000000000000000000000000000000\n0F3F7046C9909090000F3F3970606060\n00C0E020309090900000C0C0E0606060\nFFFF809F90909F9F007F7F606060607F\n00C0E020B090A0200000C0C06060C0C0\n0F3F7047C8884F67000F3F387070381F\n00C0E030F00000C00000C0E000000000\nFFFF80F909090909007F7F0606060606\nF0F010F00000000000E0E00000000000\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F09090909090900060606060606060\nF0F059492F26101000603030191F0F0F\nF0F0A020404080800060C0C080800000\n909090984F47300F60606070383F0F00\n90909090A03010F060606060C0E0E000\n80989492919090F07F67636160606000\n4080C060309090F0800080C0E0606000\n1E01F1F15F4E300F01000060313F0F00\nE02030102020C000C0C0E0E0C0C00000\n090909090909090F0606060606060600\n00000000000000000000000000000000\n909090994F46300F60606070393F0F00\n909090902020C000606060E0C0C00000\n905949292F16100F60303010190F0F00\n90A020404080800060C0C08080000000\n969F9990868990E060666F7F79706000\n9090909010109070606060E0E0E06000\n101030266949D0F00F0F1F1930306000\n8080C0406020B0F000008080C0C06000\nF0F059492F26101000603030191F0F0F\nF0F0A020404080800060C0C080800000\nFFFF80FF0103060C007F7F0000010307\nF0F010909020408000E0E060E0C08000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\nF8F88888888888880070707070707070\n00000000000000000000000000000000\n0F3F76C9F0010306000F397000000103\n00C0E020B09020400000C0C060E0C080\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n090909090909090F0606060606060600\n00000000000000000000000000000000\n193264C89F9F80FF0E1C3870607F7F00\n00000000F0F010F00000000000E0E000\n00000070F88888700000000070707000\n00000000000000000000000000000000\n000000787850D0E00000000030206000\n00000000000000000000000000000000\n50700070F88888702000000070707000\n00000000000000000000000000000000\n060D0F00060F09060306000000060600\n80000000000000000000000000000000\n\n#15:MAIN SOUND\n6801F03200017000680200F508026000\n2800303019FE00007801F04F0AFF0000\n7801F00F02FF00000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n0800000F000000000800000F00000000\n00404007000103400002030700040340\n80040306000403060004030600010306\n00020306000440400000030300000305\n00000305000403060000030300814007\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n40404040404040404040404040404040\n170F44190F00160F44190F00190F0027\n0F00190F44250F00170F001A0F001D0F\n44190F00190F00250F00190F00190F00\n170F001E0F44160F00190F00190F001B\n0F00190F00190F00170F001A0F00250F\n47190F00190F00190F00190F00190F00\n311F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n3D1F0000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n192F00000000000000000000192F0000\n0000000000000000192F00192F000000\n00000000192F00000000000000000000\n192F00000000000000000000192F0000\n0000000000000000192F000000000000\n00000000192F00192F00192F00192F00\n461F00471F003B1F003A1F0000000000\n0000000000000000441F00451F00391F\n00381F00000000000000000000000000\n3F1F00401F00341F00331F0000000000\n00000000000000003D1F003E1F00321F\n00000000411F00000000000000000000\n5F4F005F4F005F4F005F4F005F4F005F\n4F005F4F005F4F005F4F005F4F005F4F\n005F4F005F4F005F4F005F4F005F4F00\n5F4F005F4F005F4F005F4F005F4F005F\n4F005F4F005F4F005F4F005F4F005F4F\n005F4F005F4F005F4F005F4F005F4F00\n5F4F005F4F005F4F005F4F003D3F005F\n4F005F4F005F4F005F4F005F4F005F4F\n005F4F003D3F005F4F005F4F005F4F00\n5F4F005F4F005F4F005F4F003D3F005F\n4F005F4F005F4F005F4F005F4F005F4F\n005F4F003D3F005F4F003D3F003D3F00\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000323F00000000323F00323F00\n\n"
  },
  {
    "path": "programs test/BG Scroll.nx",
    "content": "BG COPY 0,0,20,16 TO 0,0\nGAMEPAD 1\nDO\n IF LEFT TAP(0) THEN BG SCROLL 2,2 TO 17,13 STEP -1,0\n IF RIGHT TAP(0) THEN BG SCROLL 2,2 TO 17,13 STEP 1,0\n IF UP TAP(0) THEN BG SCROLL 2,2 TO 17,13 STEP 0,-1\n IF DOWN TAP(0) THEN BG SCROLL 2,2 TO 17,13 STEP 0,1\n WAIT VBL\nLOOP\n\n#1:MAIN PALETTES\n053F2F00053834000000000000000000\n00000000000000000000000000000000\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n81422418182442810000000000000000\nFFFFFFFFFFFFFFFF0000000000000000\n0000000000000000FFFFFFFFFFFFFFFF\nFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF\n183C42C3C3423C18005A004242005A00\n\n#3:MAIN BG\n000014100101010100000101000000000000\n01010101010100000101010100000000\n01010000000001010101010100000101\n01010101000001010101010100000101\n00000000010101010000010100000101\n01010000000000000200000002000000\n02000000020000000000000000000000\n00000000030001010000000001010000\n02000000000000000000000002000000\n00000000000000000000000000000101\n01010000000000000200000002000200\n02000000020000000000000003000000\n00000000000001010101010100000000\n03000000000000000000000003000000\n00000000000000000000000000000101\n00000101010100000000030002000200\n02000300000000000000000000000000\n00000200020000000000010101010000\n00000000000000000000000000000000\n02000200000000000200000000000000\n01010000010100000000000000000000\n00000000000002000000000002000200\n00000000000001010000010100000000\n00000301000000000300000000000200\n00000000000002000000000003000000\n01010101000000000000000000000000\n00000000000000000000000000000200\n00000000000001010000000001010000\n04000400040004000000000004000000\n00000000000002000000000004000101\n00000101010104000400040004000400\n04000400040004000000000000000200\n00000000000001010000000001010300\n00000000040004000400040004000400\n04000400000000000000030100000000\n01010101000001010101010100000101\n00000101010101010101010101010000\n00000101010101010101010101010000\n01010101010101010101010101010000\n01010000000001010101010100000101\n0101\n\n"
  },
  {
    "path": "programs test/Crazy Text.nx",
    "content": "RANDOMIZE TIMER\n\nPRINT \"++++++++++++++++++++\";\nPRINT \"....................\";\nPRINT \"////////////////////\";\nPRINT\nPRINT \"        HELLO!\"\nPRINT \" THIS HAPPENDS WHEN\"\nPRINT \"   YOU PRINT SOME \"\nPRINT \" TEXT ON SCREEN AND\"\nPRINT \"  THEN CHANGE THE\"\nPRINT \"  CELL ATTRIBUTES\"\nPRINT \"      RANDOMLY.\"\nPRINT\nPRINT\nPRINT \"////////////////////\";\nPRINT \".....LOWRES  NX.....\";\nPRINT \"++++++++++++++++++++\";\n\nDO\n  ATTR (RND*4,RND*2,RND*2,0,0)\n  BG FILL RND*10,RND*8 TO 10+RND*10,8+RND*8\n  WAIT 4\nLOOP\n"
  },
  {
    "path": "programs test/Demo 0.5.nx",
    "content": "'** SCROLLING BACKGROUND AND SPRITES DEMO 0.5 **\n\nGLOBAL TICK,WATERY\n\nBG 0\nBG COPY 0,0,32,16 TO 0,0\nBG 1\nBG COPY 0,16,32,16 TO 0,0\n\nON RASTER CALL RASTERFX\nGAMEPAD 1\n\nBG 1\nATTR (7,,,1)\nTEXT 0,0,\"SCORE\"\n\n'INIT SPRITE\nSPRITE.A 0,(6,,,,1)\n\nWATERY=112\n\nJUMP=0\nPX=32\nPY=64\nVY=0\n\nDO\n TICK=TICK+1\n \n 'PLAYER CONTROL\n IF LEFT(0) THEN PX=PX-1\n IF RIGHT(0) THEN PX=PX+1\n IF PY=64 AND (BUTTON TAP(0,0) OR BUTTON TAP(0,1)) THEN VY=-8\n \n 'ANIMATE PLAYER SPRITE\n PY=PY+VY/2\n IF PY=64 THEN\n  VY=0\n  FR=130+((TICK/8) MOD 3)*2\n ELSE\n  VY=VY+0.5\n  FR=136\n END IF\n SPRITE 0,PX,PY,FR\n \n 'SCROLL MAIN PLANE\n SCROLL 0,TICK,0\n \n 'WATER LEVEL\n WATERY=INT(112+SIN(TICK*0.01)*12)\n \n 'FAKE SCORE COUNTER\n NUMBER 15,0,TICK/20,5\n \n WAIT VBL\nLOOP\n\n\nSUB RASTERFX\n '3D SCROLLING, SKY COLORS AND UNDER WATER\n IF RASTER=0 THEN\n  SCROLL 1,0,0\n  COPY ROM(1),SIZE(1) TO $FF00\n  PALETTE 0,%110111,,,\n  EXIT SUB\n END IF\n IF RASTER=WATERY THEN\n  COPY ROM(5),SIZE(5) TO $FF00\n  EXIT SUB\n END IF\n IF RASTER>WATERY-1 THEN\n  OFFS=SIN((RASTER+TICK*0.5)*0.1)*4\n  SCROLL 1,TICK*1.5+OFFS,0\n  SCROLL 0,TICK+OFFS,0\n  EXIT SUB\n ELSE IF RASTER<8 THEN\n  SCROLL 1,0,0\n ELSE IF RASTER<32 THEN\n  IF RASTER=14 THEN PALETTE 0,%100111,,,\n  IF RASTER=28 THEN PALETTE 0,%010111,,,\n  SCROLL 1,TICK*3/8,0\n  EXIT SUB\n ELSE IF RASTER<56 THEN\n  IF RASTER=42 THEN PALETTE 0,%000111,,,\n  SCROLL 1,TICK/4,0\n ELSE IF RASTER>56+3*8 THEN\n  SCROLL 1,TICK*1.5,0\n ELSE\n  SCROLL 1,TICK*0.5*(0.5+(RASTER-55)/32),0\n END IF\nEND SUB\n\n\n#1:MAIN PALETTES\n07382410000C0804000F0B07003F2F1B\n003C38340030201000103039003F1500\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n0020040000440002FFDFFFFFFFFBFFFF\nFFFFFF77220088DD00000088DDFFFFFF\nC0E0E8E8E2C8E0C23F1F17171D371F3D\n01094B0B030B0123FFFFFFFFFFFFFFFF\n030C12204044808000030F1F3F3F7F7F\n8082404820100C03FFFF7F7F3F1F0F03\n0002000010000000FFFFFFFFFFFFFFFF\n0083443800834438FFFFFFFFFFFFFFFF\n0083443800834438000083C7FFFFFFFF\n08080808088B442818181818DBFF7C38\n080808080808080818181A1C18583818\n30382743C2E41C180000183C3C180000\n00000000001044550000000000000000\n7E81A3818985A17E007F5D7F777F7F7E\n10220C3870C40802FFFDF3C78F3FFFFD\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n030C10204040808000030F1F3F3F7F7F\n8080404020100C03FFFF7F7F3F1F0F03\n0000000000000000FFFFFFFFFFFFFFFF\n00000000000000FFFFFFFFFFFFFFFFFF\n0304041820204080000303071F1F3F7F\n804030080402020180C0F0F8FCFEFEFF\n0000000000000000FFFFFFFFFFFFFFFF\n007300E600700238FFFCFF1FFFFFFFC7\n00801800010C2000FF7FFFFFFFF3FFFF\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0307070F1F1F07050000010407070302\nE0F8F0FCFCF8F0C00000B0FCDC80F020\n000307070F1F1F070000000104070703\n00E0F8F0FCFCF8F0000000B0FCDC80F0\n000307070F1F1F070000000104070703\n00E0F8F0FCFCF8F0000000B0FCDC80F0\n070F0F1F3F3F0F1F000003090F0F0700\nC0F0E0F8F8F0E040000060F8B800E080\n00070F0F1F3F3F0F00000003090F0F03\n0EDEFEFEFEFCFCF80E160E60F8B800E0\n00000000000307070000000000000001\n0000000000E0F8F000000000000000B0\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0D1D1E1C0C000E1E0202031F0F0F0000\nD0D83C1C1800383C2020E0FCF8780000\n0F1F38301818180C00003F3707070000\n5C5C983838380000BCBCF0C0C0800000\n0B13033030302000040C1C0F0F0C0000\n00D8FCFC80007078C0181C1C60E00000\n1707000001050E0708181F0F0E020000\nC0E0600080C040004060E0E060000000\n3E3E3C1C6663604001091F1B191C1E00\nB0A08206068600004050F0F8F8780000\n0F0F0F1B1F1C0E1E04070307060F0100\nFCFCF8FCFC0C1C1EFCDC80F028FCE000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n183C3C3C3C183C181824242424182418\n6CFEFE7E240000006C92925A24000000\n247EFF7E7EFF7E24245A815A5A815A24\n083E7F7E3F7F3E080836414631413608\n62F7FE7C3E7FEF4662959A742E59A946\n1C3E7E7EFFFE7F3A1C224A46919A453A\n183C3C78300000001824244830000000\n0C1E3C78783C1E0C0C1224484824120C\n30783C1E1E3C78303048241212244830\n00247E7EFF7E7E2400245A6681665A24\n00183C7EFF7E3C180018246681662418\n000000183C3C78300000001824244830\n0000007EFF7E00000000007E817E0000\n00000000183C3C180000000018242418\n060F1E3C78F0E040060912244890A040\n3C7EFFFFFFFF7E3C3C4299918999423C\n183C7C3C3C7EFF7E182444242466817E\n3C7EFF7E3C7EFF7E3C429972244E817E\n3C7EFF7E6FFF7E3C3C4299726999423C\n66FFFFFF7F0F0F066699998179090906\n7EFFFEFE7F7FFE7C7E819E827979827C\n1C3E7CFEFFFF7E3C1C224C829999423C\n7EFF7F1E3C7878307E81791224484830\n3C7EFF7EFFFF7E3C3C4299429999423C\n3C7EFF7F7FFF7E3C3C4299417999423C\n0000183C183C18000000182418241800\n0000183C183C78300000182418244830\n000C1E3C783C1E0C000C12244824120C\n00007EFF7EFF7E0000007E817E817E00\n0030783C1E3C78300030482412244830\n3C7EFF7E3C183C183C42997224182418\n3C7EFFFFFFFE7E3C3C429991919E423C\n183C7EFFFFFFFF661824429981999966\n7CFEFFFEFFFFFE7C7C8299829999827C\n3C7EFFF6F6FF7E3C3C4299969699423C\n78FCFEFFFFFEFC787884929999928478\n7EFFFEFCF8FEFF7E7E819E84989E817E\n7EFFFEFCF8F0F0607E819E8498909060\n3C7EFEFFFFFF7E3C3C429E919999423C\n66FFFFFFFFFFFF666699998199999966\n3C7E3C3C3C3C7E3C3C4224242424423C\n1E3F1F0F6FFF7E3C1E2119096999423C\n66FFFEFCFCFEFF666699928484929966\n60F0F0F0F0FEFF7E60909090909E817E\n42E7FFFFFFFFFF6642A5998181999966\n66FFFFFFFFFFFF666699898191999966\n3C7EFFFFFFFF7E3C3C4299999999423C\n7CFEFFFEFCF0F0607C8299829C909060\n3C7EFFFFFFFE7F3E3C4299999592413E\n7CFEFFFEFCFEFF667C82998284929966\n3E7FFE7E3F7FFE7C3E419E423979827C\n7EFF7E3C3C3C3C187E81662424242418\n66FFFFFFFFFF7E3C669999999999423C\n66FFFFFFFF7E3C186699999999422418\n66FFFFFFFFFFE742669999818199A542\n66FF7E3C7EFFFF666699422442999966\n66FFFF7E3C3C3C186699994224242418\n7EFF7E3C78FEFF7E7E817224489E817E\n3C7E7C78787C7E3C3C424C48484C423C\n60F0783C1E0F07026090482412090502\n3C7E3E1E1E3E7E3C3C4232121232423C\n183C7EFF660000001824429966000000\n00000000007EFF7E00000000007E817E\n\n#3:MAIN BG\n00002020000000000501070905090000\n00200000000000000000000000000000\n00000000000000000000000000000000\n05010701070105090000000000000000\n00000000000005010701070107010529\n05210729052900200020000000000000\n00000000000000000000000000000521\n07210721072107210529000000000000\n00000000050107010701070107010721\n07290729072905290020000000000000\n00000000000000000000000000000721\n07210721072107210721000000000000\n00000000070107010701070107010721\n07290729072907290529000000000000\n00000000000000000000000000000621\n07210721072107210629000000000000\n00000000060107010701070107010729\n0729072907290729062900000E000E00\n0E000E00000000000000000000000000\n062107210721062900000E000E000E00\n00000000000006010701070106090721\n07290729072906290000000000000000\n00000000000000000000000000000000\n000003A004A000000000000000000000\n00000000000000000300040000000621\n07A107A106290E000E00000000000000\n00000000000000000000000000000000\n000003A004A000000000000000000000\n00000000000000000300040000000000\n03A004A0000000000000000000000000\n00000CA4000000000000000000000000\n000003A004A000000000000000000000\n0E000E00000000000300040000000080\n03A004A0000000000000000000000000\n0C040BA100000C040000000000000000\n000003A004A000000000000000000000\n000000000D810D81030004000DA10DA1\n03A004A00DA10DA100000DA10DA10D21\n0B010BA10D010B010D210D210D210D21\n0D2103A004A000200DA10DA100200000\n00000000020102010201020102010201\n02010201020102010201020102010201\n02010201020102010201020102010201\n02010201020102010201020102010201\n02010201010001000100010001000100\n01000100010001000100010001000100\n01000100010001000100010001000100\n01000100010001000100010001000100\n01000100010001000100010001000100\n01000100010001000100010001000100\n010001000F0001000100010001000100\n01000100010001000100010001000100\n0F000100010001000F00010001000100\n01000100010001000100010001000100\n01000100010001000100010001000100\n01000F00010001000100010001000100\n01000100010001000100010001000100\n01000100010001000100010001000100\n01000100010001000100010001000100\n010001000100010001000F0001000100\n01000100010001000100010001000100\n0100010001000F000100010001000100\n01000100010001000F00010001000100\n01000100010001000100010001000100\n01000100000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n0000000B000B00000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000004003\n4203400B000000000000000000000000\n400342034203400B0000000000000000\n00000000000000000000000000000000\n00000000000000000000000040034203\n420342034203400B0000000000000000\n410343034303410B0000000000000000\n00000000000000000000000000000000\n00000000000000000000000041034303\n430343034303410B0000000000000000\n00000000000000000000000000000000\n000000000000000040034203400B0000\n00000000000000000000000000000000\n00000000000000000000000000004003\n4203400B000000000000000000000000\n0000000000004003420342034203400B\n00000000000000000000000000000000\n00000000000000000000000040034203\n42034203400B00000000000000000000\n0000000000004103430343034303410B\n00000000000000000000000000000000\n00000000000000000000000041034303\n43034303410B00000000000000000000\n00000000470247024702470247024702\n47024702470247024702470247024702\n47024702470247024702470247024702\n47024702470247024702470247024702\n47024702480248024802480248024802\n48024802480248024702480246024802\n48024702460248024802480247024802\n4802460A4802460A460A460A460A4802\n480248024802460A4802460248024802\n460A4602480248024802460A48024802\n48024602460A460A4802460A460A4802\n460A48024602460A48024802460A4802\n460248024602460A460A480246024802\n460A460A48024602460A48024802460A\n4802460A460A4802460A480246024802\n4802460A46024802460A460A460A4802\n480248024802460A4802460248024802\n460A4602480248024802460A48024802\n480246024702460A4802460248024802\n460A48024602460A48024802460A4802\n4602480246024602460202A102A14602\n46024602460246024602460246024602\n46024602460246024602460246024602\n46024602460202A102A1460246024602\n4602460246024602460203A004A04602\n46024602460202A102A1460246024602\n4602460202A102A14602460246024602\n46024602460203A004A0460246024602\n4602460246024602460203A004A04602\n46024602460203A004A0460246024602\n4602460203A004A046024602460202A1\n02A14602460203A004A0460246024602\n4602460246024602460203A004A04602\n46024602460203A004A0460246024602\n4602460203A004A046024602460203A0\n04A04602460203A004A0460246024602\n46024602\n\n#5:WATER COLOR PALETTES\n070F0B07000F0B07000F0B07003F2F1B\n000F0B07000B07030003070F000F0601\n\n"
  },
  {
    "path": "programs test/Files.nx",
    "content": "FILES\nFOR I=0 TO 15\n NUMBER 0,I,I,2\n TEXT 3,I,FILE$(I)\nNEXT I\n"
  },
  {
    "path": "programs test/Scrolling Map 0.3.nx",
    "content": "'** SCROLLING MAP 0.3 **\n\n'GET MAP SIZE\nMAPW=PEEK(ROM(3)+2)\nMAPH=PEEK(ROM(3)+3)\n\n'INITIAL CAMERA POSITION\nCAMX=176\nCAMY=188\nMAPCOL=INT(CAMX/8)\nMAPROW=INT(CAMY/8)\n\n'COPY VISIBLE AREA FROM MAP MEMORY TO BACKGROUND\nBG COPY MAPCOL,MAPROW,21,17 TO MAPCOL,MAPROW\n\nGAMEPAD 1\n\nDO\n  'MOVE CAMERA\n  IF UP(0) AND CAMY>0 THEN CAMY=CAMY-1\n  IF DOWN(0) AND CAMY<MAPH*8-128 THEN CAMY=CAMY+1\n  IF LEFT(0) AND CAMX>0 THEN CAMX=CAMX-1\n  IF RIGHT(0) AND CAMX<MAPW*8-160 THEN CAMX=CAMX+1\n\n  'GET NEW MAP POSITION\n  COL=INT(CAMX/8)\n  ROW=INT(CAMY/8)\n\n  'COPY RIGHT OR LEFT EDGE FROM MAP MEMORY TO BACKGROUND\n  IF COL>MAPCOL THEN\n    BG COPY COL+20,ROW,1,17 TO COL+20,ROW\n    MAPCOL=COL\n  ELSE IF COL<MAPCOL THEN\n    BG COPY COL,ROW,1,17 TO COL,ROW\n    MAPCOL=COL\n  END IF\n\n  'COPY BOTTOM OR TOP EDGE FROM MAP MEMORY TO BACKGROUND\n  IF ROW>MAPROW THEN\n    BG COPY COL,ROW+16,21,1 TO COL,ROW+16\n    MAPROW=ROW\n  ELSE IF ROW<MAPROW THEN\n    BG COPY COL,ROW,21,1 TO COL,ROW\n    MAPROW=ROW\n  END IF\n\n  'SET BACKGROUND OFFSET (WRAPS AROUND EDGES)\n  SCROLL 0,CAMX,CAMY\n\n  WAIT VBL\nLOOP\n\n\n#1:MAIN PALETTES\n182D0424003D3824003F2A14003F1B06\n00242A150034202A00000000003F2A15\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n00000000000000000808400200202101\n030F1E1F3B3F2E05000001000400113A\nC0E0C060E8C090400010389C143C6CBC\n4200210008004200FDFFDFFFFFFFFFFF\nBBBBBBBBBBBBBBBB4444444444444444\nBBBFA7BFFFFFFFBB447C7C7C7E7E7E44\n00000000000000000000000000000000\n001870E0C141861C3C668F1F3FBFFE1C\n1E2359D0B0A00141FFFFE7EFCFDFFFBF\n0098F4E603C1F010FFFFEFDFFFFFFFFF\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000001408800200A04404\n1200010003070F072D3F1E0F03070F07\n80000000C0E0F0E07CFCF8F0C0E0F0E0\nFE8181818181817F017F7F7F7F7F7FFF\n11FF22FF88FF22FFFFFFFFFFFFFFFFFF\n7FFF7EFFFEFF7EFFFF818181B1A18181\n11FF7EFFFEFF22FFFF81BDBD81FFFFFF\n00400060000401000020F06000020F00\n02325E4EA4C08080FFFFFFFFDFBFFFFF\nD88C0486030301013F7FFF7FFFFFFFFF\n\n#3:MAIN BG\n0000404009020A0209020A0209020A02\n09020A0209020A0209020A0209020A02\n09020A0209020A020403040309020902\n0A0209020A0209020A0209020A020902\n0A0209020A0209020A0209020A020902\n09020A0209020A0209020A0209020A02\n09020A0209020A02090209020A020902\n0A02090209020A0209020A0209020A02\n0403040319021A0219021A0219021A02\n19021A0219021A0219021A0219021A02\n19021A0219021A020403040319021902\n1A0219021A0219021A0219021A021902\n1A0219021A0219021A0219021A021902\n19021A0219021A0219021A0219021A02\n19021A0219021A02190219021A021902\n1A02190219021A0219021A0219021A02\n0403040309020A0209020A0209020A02\n09020A0209020A0209020A0209020A02\n09020A02000000000403040318020000\n01000000000018020000000000000000\n00000000080200000000110011000100\n01000100000002000300180200000200\n03000200030000000000080218020200\n03000000010002000300180204010401\n0403040319021A0219021A0219021A02\n19021A0219021A0219021A0219021A02\n19021A02180200000403040300000000\n00000200030000000000180202000300\n00000000000000000000000001000000\n00000000000012001300000000001200\n13001200130001000000180200001200\n13000000000012001300000004010401\n0403040309020A0209020A0209020A02\n18020000080200000000000000000000\n18020000080200000403040301000000\n00001200130001000100000012001300\n08020000180200000200030000000802\n00000802000000001802020003001402\n14021402140214021402140214021402\n14021402140214021402140204010401\n0403040319021A0219021A0219021A02\n00000000000002000300000018020802\n00000000000000000403040300000000\n18020000000001000000000000000000\n00000000000000001200130001000100\n18020000010001000000120013001402\n01000000010005050505050500001802\n00000000000000000000000004010401\n0403040309020A0209020A0209020A02\n00000200030012001300020003000000\n00000000000000000403040304030000\n18020000000000000200030002000300\n02000300000018020000000001000000\n00000000010001000000000000001402\n01001100010005050505050500000000\n05050505050505050000040104010401\n0403040319021A0219021A0219021A02\n01001200130002000300120013000200\n03000000180200000000040304030000\n00000802000000001200130012001300\n12001300000000000802010000000000\n02000300000001001100010000001402\n00000000000015041604170400000000\n05050505050505050000040104010403\n0403040309020A0209020A0209020A02\n02000300000012001300000000001200\n13000000000008020000040304030000\n00000000000000000000020003000200\n03000000010000000000000000000000\n12001300000001000100000000001402\n00001802000000000401000000000000\n05050505050505050000040104010403\n0403040319021A0219021A0219021A02\n12001300000000000000020003000000\n00000000110000000000040304030000\n00000000010001000000120013001200\n13000000010000000200030000000000\n00000000010001000100000000001402\n00000000000000000401000018020000\n15041704160417040000040104010403\n0403040309020A0209020A0208020000\n00000100020003000000120013000000\n02000300000011000100040304030000\n00000000010000000000000000000000\n00000100010000001200130000000100\n00000100110000000100000000001402\n00000505050505050401040104010401\n04010401040104010401040104010403\n0403040319021A0219021A0218020000\n00000000120013000802000018020000\n12001300000001000000040304030000\n01000100010001000100010001000100\n00000000000000000000010001000000\n00000000000000001100010001001402\n00000505050505050401000000000000\n00000000000000000000040104010403\n0403040309020A0209020A0200000200\n03000000000000000000010011000100\n00000000000000000000040304030000\n00000100110001000000010000000200\n03000000010000001100010000000200\n03000000000000000100010000001402\n00001704160417040401000005050505\n05050505000018020000040104010403\n0403040319021A0219021A0200001200\n13000000180200000100000000000000\n00000200030000000000040304030000\n00000100010001000100000000001200\n13000000010001000100000000001200\n13000000010001000000000000001402\n00000401040104010401000005050505\n05050505000000000000040104010403\n0403040309020A021802000000000000\n00000100010000000200030000000100\n00001200130000000000040304030000\n00000100110000000000000001000000\n00000000010001000000000000000000\n00000000010000000000000000001402\n00000401040104010401000005050505\n05050505000000000401040104010403\n0403040319021A020000020003000000\n08020000110000001200130000000000\n00000000000000000000040304030000\n00000000000000000000010001000100\n00000000000000000000000002000300\n00000000000000000000000000001402\n00000401040104010401000015041604\n17041504000000000401040104030403\n0403040309020A020000120013001100\n00000000000000000000110000000000\n02000300000000000000040304030000\n00000000000000000100010011000100\n00000000000000000000000012001300\n00000200030000000000000000000401\n04010401040104010401040104010401\n00000000000000000401040104030403\n0403040319021A020802000000000000\n02000300000001000100000000000000\n12001300000000000000040304030403\n00000000000001000100000000000100\n00000000000002000300000000000000\n00001200130000000000000004010401\n00001402140214021402140214021402\n14021402140214020401040104030403\n0403040309020A020000000000001802\n12001300000001000000020003000000\n00000000110000000000040304030403\n00000000000000000000000001000100\n00000000000012001300000000000100\n01000000000000001100000004010000\n00000000000001000000000000000200\n03000000000000000401040104030403\n0403040319021A020000020003000000\n00000000010001000000120013000000\n02000300000000000000000004030403\n00000000000011000100000000000100\n01000100000000000000010011000100\n01001100020003000000000004010000\n00000000010001000000000000001200\n13000000010000000401040104030403\n0403040309020A020000120013000000\n18020100110000000200030000000000\n12001300000000001100010004030403\n00000100000001000100010001000000\n01000100000001000100010001000000\n01000000120013000000000004010000\n00001100010000000200030000000000\n00000100000000000401040104030403\n0403040319021A020000080200000000\n00000000000000001200130000000000\n00000000010001000000000004030403\n00000100010000000000010000000100\n11000100010000000000020003000000\n01000000000000000000000004010000\n01000100010000001200130000000000\n00000000000000000401040104030403\n0403040309020A020000000000000000\n02000300000002000300000011000100\n01000100000000000000000004030403\n01000100010000000000010001000100\n01000100010000000000120013000000\n00000000000000000000000004010000\n01000100000002000300020003000100\n00000000000000000401040104030403\n0403040319021A020200030000000000\n12001300000012001300010001000100\n01000000020003000000000004030403\n00000100010000000100010001000000\n00000000000000000000000000000000\n00000000000000000000040104010000\n11000100000012001300120013000000\n02000300000000000401040104030403\n0403040309020A021200130000000000\n00000000020003000000000001000100\n01000000120013000000000004030403\n00000000000000000000000000000100\n00000000000000000000000011000000\n02000300000000000000040100000100\n01000000020003000000000000000000\n12001300000000000401040104010403\n0403040319021A020000000000000100\n00000000120013001100000000000000\n00000100000000000000000004030403\n00000000000000000000000000000000\n00000000000000000000000000000000\n12001300000000001100040100000100\n00000000120013000000010001000000\n00000000000000000000040104010403\n0403040309020A021802000002000300\n00000000000001000000000002000300\n00000100000001001100000004030403\n00000000000005050505050500000000\n00000505050505050505000000000000\n00000000000000000000040100000000\n02000300000000000100010000000000\n00000000010000000000040104010403\n0403040319021A020000000012001300\n00001100010001000000000012001300\n00000000010001000000000004030403\n00000000000005050505050500001802\n00000505050506050505000000001802\n05050505050500000000040100000000\n12001300000000000000000002000300\n00000100110001000000040104010403\n0403040309020A020000000000000000\n11000000000000000000000000000000\n00000100110001000000000004030403\n00000000000015041604170400000000\n00000505050505050505000000000000\n05050505060500000000040100000000\n00000000000000000000000012001300\n00000000010000000000040104010403\n0403040319021A020000110000000000\n00000000000000000000000000000000\n00000100000000000000000004030403\n04031802000000000401000000000000\n00001504170416041704180200000000\n17041604170400000000040100000000\n01000100010002000300000000000000\n00000100010001000000040104010403\n04030403040304030403040304030403\n04030403040304030403040300000000\n00000000000000000000000004030403\n04030000000000000401040104010401\n04010401040104010401040104010401\n04010401040100000000040100000100\n11000100000012001300000000000802\n00000000010000000000040104010403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403000000000403\n04030000000000000000000000000000\n00001802000000000000000000000000\n04010401040104010000040100000100\n00000000000018020000000001000000\n00000100010000000000040104010401\n0403040309020A020000000000000000\n00000000000000000403040304030403\n04030403040304030403040304030403\n04030403000000000000050505050505\n00000000000005050505050505050000\n04010401040104010401040100000000\n00000000080200000100000000000100\n00000000000000001802000004010401\n0403040319021A020000010000000000\n00000100110000000000000000000000\n00000000000004030403040304030403\n04030403000018020000050505050505\n00000000000005050505050505050000\n04010401040104010000000000000100\n01001802000001001100010001000000\n08020000020003000000010004010401\n0403040309020A020100110002000300\n00000100010001000000000000000000\n00000000000000000000000000000403\n04030403040300000000170416041704\n00000000000015041604170415040000\n04010401040104010000000001000100\n000009020A0209020A0209020A020000\n00000100120013000100010004010401\n0403040319021A020100010012001300\n00000100010000000200030000000000\n00000000080200000000180200000000\n04030403040304030000000004010401\n04010401040104010401040104010401\n04010401040104010000000001001100\n000019021A0219021A0219021A020000\n01000000000001000100000004010401\n0403040309020A020100000000000000\n01000100000000001200130000000000\n08020802000000000802000000000000\n00000403040304030403000018020000\n00000401040104010000000000000000\n00000000000018020000010000000902\n0A0209020A0209020A0209020A020902\n0A020802000000000000000004010401\n0403040319021A020000020003000000\n00000000000000000000000000001802\n00000000000000000000000000000000\n00000000040304030403000000000000\n00000401040104010000000000000000\n01000000000000000000000000001902\n1A0219021A0219021A0219021A021902\n1A020000000002000300000004010401\n0403040309020A021802120013000802\n00000200030000000000010000000000\n00000000000001000100110000000000\n00000000000004030403040300000000\n01000401040104010000000001000100\n00000000000008020000180209020A02\n09020A0209020A0209020A0209020A02\n08020000180212001300000004010401\n0403040319021A020000000000000000\n01001200130000000100010000000000\n02000300000000000100010000000000\n00000000000004030403040300000000\n00000401040104010000000001000000\n00000000000000000000000019021A02\n19021A0219021A0219021A0219021A02\n18020000000000000000000004010401\n0403040309020A020000000001000100\n00000000000001000100110000000000\n12001300000000000100000011000000\n01000100000000000403040304030000\n00000000040100000000000000000100\n01000100000000000802000009020902\n0A0209020A0209020A02180200000000\n00000000110000000000010004010401\n0403040319021A020000080200000000\n00000802000000000000010001000100\n00000000010001000000000000000000\n01000100000000000403040304030000\n01000000040100000000010001000000\n01000100000018020000000019021902\n1A0219021A0219021A02180205050505\n05050000000001000100000004010401\n0403040309020A021802000002000300\n00000000000018020000000001000100\n00000000010000000000000000000000\n01000000000000000403040304030000\n00000000040100000100010001000100\n01000000000000000100010000000902\n0A0209020A0209020A02000005050505\n05050000010000000000000004010401\n0403040319021A020000000012001300\n00000100000002000300000000000000\n00000000000011000000020003000000\n01000100000000000000040304030403\n00000000040100000000000000000000\n00000000080200000000000000001902\n1A0219021A0219021A02000015041604\n17040000000001000000000004010401\n0403040309020A020200030000000000\n00000100000012001300000001001100\n01000000000000000000120013000000\n01000100000001000000040304030403\n00000000040100000000020003000000\n01000000000000000100000008020000\n09020A0209020A0209020A0204010401\n04010000000000000000000004010401\n0403040319021A021200130000000200\n03000000000000000000010000000100\n01000000020003000000000000000100\n00000000000000000000040304030403\n00000000040100000000120013000000\n01000000010001000100000018020000\n19021A0219021A0219021A0204010401\n04010401040104010401040104010401\n0403040309020A021802020003001200\n13000000000000000100010001000000\n00000000120013000000000000000100\n01000000000000001402140214021402\n14020000040100000000000000000000\n01000100000001000000000000000100\n09020A0209020A0209020A0200000401\n00000000000000000000040104010401\n0403040319021A020000120013000000\n00000000000001000100000001001100\n01000000010000000000010001000000\n00000000040104010401040104010401\n04010401040100000200030002000300\n00000000010000000000080200000000\n19021A0219021A0219021A0200000000\n18020000000000000000040104010403\n0403040309020A020200030000000000\n00000000010000000000010001000000\n00000000000000000000010000000000\n00000000040100001402140214021402\n14020000000000001200130012001300\n00000100000000001802000001000000\n09020A0209020A0209020A0202000300\n00000000020003000000040104010403\n0403040319021A021200130018020100\n00000505050505050505050500000000\n00001802000000000000000000000000\n00000000040100000000040304030403\n00000100000000000000020003000000\n00000100010011000000010001000000\n19021A0219021A0219021A0212001300\n00001802120013000000040104010403\n0403040309020A020000000001000100\n00000505050505050605050500001802\n05050505050505050505060500000000\n00000000040100000000040304030403\n00000000010000000000120013000000\n00000100010000000200030000000000\n180209020A0209020A02180200000200\n03000000000000000000040104010403\n0403040319021A021100010001000000\n00000505050505050505050500000000\n05050505050505050505050500000000\n00000000040100000000040304030403\n00000100010000000100000000000100\n01000100000000001200130000000000\n000019021A0219021A02180200001200\n13000100110000000000040104010403\n0403040309020A020000010000000000\n00001504170415041604150400000000\n15041604170415041604170400000000\n00000000040100000000040304030403\n00000100010001001100010001000100\n01000000000018020000000008020000\n09020A0209020A0209020A0202000300\n18020100000000000000040104010403\n0403040319021A021802000000000000\n18020000000000000402000000000000\n00000402000000000402000000000000\n00000000040100000000040304030403\n00000000000001000100010000000100\n00000200030000000000000000000000\n19021A0219021A0219021A0212001300\n00000802000000000401040104010403\n0403040309020A020000050505050505\n00000000110000000402040204020402\n04020402040204020402040204010401\n04010401040100000000040304030403\n00000000110001000100000000000100\n00001200130008020000000018020000\n09020A0209020A0209020A0209020A02\n18020000000000000401040104030403\n0403040319021A020000050505050605\n00001100000000000402040204020402\n04020402040204020402040200000000\n00000000010000000000040304030403\n00000000010000000000000001001100\n01000000000000000000000000000000\n19021A0219021A0219021A0219021A02\n01000000000004010401040104030403\n0403040309020A020000150416041704\n00000000000000000402040204020402\n04020402040204020402040200000000\n01000100010000000000040304030403\n00000100000002000300000001000100\n00000000000001000000000008020000\n000009020A0209020A0209020A020000\n00000000040104010401040304030403\n0403040319021A020000000004020402\n04020402040204020402140214021402\n14021402140214021402140200000100\n11000000000000000401040304030403\n00000000000012001300000001000000\n00000200030001000100010000000000\n000019021A0219021A0219021A020000\n00000401040104010403040304030403\n0403040309020A020401040104010401\n04010401040104010401140204010401\n04011402040104010401140200000000\n00000000000004010401040304030403\n04030000000001000100010001000000\n00001200130001001100010000000000\n00000000180200001802000000000000\n04010401040104030403040304030403\n0403040319021A020401040104010401\n04010401040104010401040104010401\n04010401040104010401040104010401\n04010401040104010403040304030403\n04030401040100000000000001000100\n01000000000000000000000000000401\n04010401040104010401040104010401\n04010401040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304010401\n04010401040104030403040304030403\n04030401040104010401010001000000\n00000000040104010401040104010401\n04010401040104010401040104010401\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040104010401040104010401\n04010401040104010401040104010401\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030401040104010401\n04010401040104030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403040304030403040304030403\n04030403\n\n"
  },
  {
    "path": "programs test/Slide Show 0.4.nx",
    "content": "GLOBAL OFY,SCY,FXT\n\nDISPLAY (1,1,0)\n\nON RASTER CALL FX\n\nNEXTPAGE:\nIF OFY=0 THEN OFY=16 ELSE OFY=0\nBG FILL 0,OFY TO 19,OFY+15 CHAR 0\nROW=0\nREPEAT\n READ T$\n IF ROW=1 THEN ATTR (1) ELSE ATTR (0)\n IF T$<>\"]\" THEN TEXT 0,OFY+ROW,T$\n ROW=ROW+1\nUNTIL T$=\"]\"\nFOR I=0 TO 180\n FXT=I/180\n WAIT VBL\nNEXT I\nFXT=0\nSCY=SCY+128\nDO\n IF TAP THEN GOTO NEXTPAGE\n WAIT VBL\nLOOP\n\nSUB FX\n T=MIN(MAX(0,FXT*1.25-RASTER/384),1)\n F=T*T*(3-2*T)\n X=SIN(F*PI*2)*8\n Y=F*128+SCY\n SCROLL 0,X,Y\nEND SUB\n\n\nSLIDES:\nDATA \"\"\nDATA \"\"\nDATA \"\"\nDATA \"\"\nDATA \"\"\nDATA \"\"\nDATA \"********************\"\nDATA \"     LOWRES  NX\"\nDATA \"********************\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"     LOWRES  NX\"\nDATA \"********************\"\nDATA \"\"\nDATA \" - FANTASY CONSOLE\"\nDATA \"\"\nDATA \" - PROGRAMMABLE IN\"\nDATA \"   BASIC\"\nDATA \"\"\nDATA \" - AUTHENTIC 8-BIT\"\nDATA \"   SYSTEM\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"  FANTASY  CONSOLE\"\nDATA \"********************\"\nDATA \"\"\nDATA \" - HANDHELD CONSOLE\"\nDATA \"\"\nDATA \" - CARTRIDGES\"\nDATA \"\"\nDATA \" - TOUCHSCREEN\"\nDATA \"   (160X128)\"\nDATA \"\"\nDATA \" - TWO GAME\"\nDATA \"   CONTROLLERS\"\nDATA \"\"\nDATA \" - KEYBOARD\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"        PLAY\"\nDATA \"********************\"\nDATA \"\"\nDATA \" - OPEN NX FILE\"\nDATA \"\"\nDATA \" - GAMEPAD:\"\nDATA \"   CURSOR KEYS\"\nDATA \"   BUTTON A: Z\"\nDATA \"   BUTTON B: X\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"    HELLO  WORLD\"\nDATA \"********************\"\nDATA \"\"\nDATA \" TEXT FILE:\"\nDATA \" HELLO WORLD.NX\"\nDATA \"\"\nDATA \" - PRINT\"\nDATA \"\"\nDATA \" - INPUT\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"       ASSETS\"\nDATA \"********************\"\nDATA \"\"\nDATA \" SPRITE FOR\"\nDATA \" HELLO WORLD\"\nDATA \"\"\nDATA \" GRAPHICS BASED ON\"\nDATA \" 8x8-PIXEL\"\nDATA \" CHARACTERS\"\nDATA \"\"\nDATA \"  CHARACTER\"\nDATA \"          DESIGNER\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"      NX FILES\"\nDATA \"********************\"\nDATA \"\"\nDATA \" INCLUDE:\"\nDATA \"\"\nDATA \" - SOURCE CODE\"\nDATA \"\"\nDATA \" - ASSETS\"\nDATA \"   (16 ROM ENTRIES)\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"    HELLO  WORLD\"\nDATA \"********************\"\nDATA \"\"\nDATA \" SHOW SPRITE:\"\nDATA \"\"\nDATA \" - SPRITE N,X,Y,C\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"    HELLO  WORLD\"\nDATA \"********************\"\nDATA \"\"\nDATA \" ANIMATE:\"\nDATA \"\"\nDATA \" - DO/LOOP\"\nDATA \"\"\nDATA \" - WAIT VBL\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"    HELLO  WORLD\"\nDATA \"********************\"\nDATA \"\"\nDATA \" MOVE:\"\nDATA \"\"\nDATA \" - GAMEPAD N\"\nDATA \"\"\nDATA \" - UP(P)\"\nDATA \" - DOWN(P)\"\nDATA \" - LEFT(P)\"\nDATA \" - RIGHT(P)\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"       ASSETS\"\nDATA \"********************\"\nDATA \"\"\nDATA \" BACKGROUND FOR\"\nDATA \" HELLO WORLD\"\nDATA \"\"\nDATA \" MORE CHARACTERS...\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"       ASSETS\"\nDATA \"********************\"\nDATA \"\"\nDATA \"  BACKGROUND\"\nDATA \"          DESIGNER\"\nDATA \"\"\nDATA \" - MAPS AND COLORS\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"    HELLO  WORLD\"\nDATA \"********************\"\nDATA \"\"\nDATA \" SHOW BACKGROUND:\"\nDATA \"\"\nDATA \" - BG N\"\nDATA \"\"\nDATA \" - BG COPY X,Y,W,H\"\nDATA \"   TO X,Y\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"   SCREEN  LAYERS\"\nDATA \"********************\"\nDATA \"\"\nDATA \" - NO PIXEL BUFFER\"\nDATA \"\"\nDATA \"       +-BACKDROP--+\"\nDATA \"     +-BG1-------+ I\"\nDATA \"   +-BG0-------+ I I\"\nDATA \" +-SPRITES---+ I I I\"\nDATA \" I           I I I I\"\nDATA \" I           I I I I\"\nDATA \" I           I I I-+\"\nDATA \" I           I I-+\"\nDATA \" I           I-+\"\nDATA \" +-----------+\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"    HELLO  WORLD\"\nDATA \"********************\"\nDATA \"\"\nDATA \" SCROLL BACKGROUND:\"\nDATA \"\"\nDATA \" - DISPLAY BG,X,Y\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"       ASSETS\"\nDATA \"********************\"\nDATA \"\"\nDATA \"  BACKGROUND\"\nDATA \"          DESIGNER\"\nDATA \"\"\nDATA \" - PRIORITIES\"\nDATA \"]\"\n\nDATA \"********************\"\nDATA \"        MORE\"\nDATA \"********************\"\nDATA \"\"\nDATA \" DOCS/\"\nDATA \" INTRODUCTION.TXT\"\nDATA \"\"\nDATA \" TWITTER:\"\nDATA \" @TIMO_INUTILIS\"\nDATA \"]\"\n\nDATA \"]\"\n"
  },
  {
    "path": "programs test/Sprites with Background 0.3.nx",
    "content": "'** SPRITES WITH BACKGROUND 0.3 **\n\n'COPY MAP TO BACKGROUND\nBG COPY 0,0,20,16 TO 0,0\n\n'INIT SPRITE PALETTES AND SIZES\nSPRITE.A 0,(6,,,,0)\nSPRITE.A 1,(6,,,,3)\n\nPX=76\nPY=88\nMX=64\nMY=8\n\nGAMEPAD 1\n\nDO\n  TICK=TICK+1\n\n  'PLAYER CONTROL\n  IF UP(0) AND PY>0 THEN PY=PY-1\n  IF DOWN(0) AND PY<120 THEN PY=PY+1\n  IF LEFT(0) AND PX>0 THEN\n    PX=PX-1\n\tSPRITE.A 0,(,1)\n  END IF\n  IF RIGHT(0) AND PX<152 THEN\n    PX=PX+1\n    SPRITE.A 0,(,0)\n  END IF\n\n  'PLAYER ANIMATION\n  IF UP(0) OR DOWN(0) OR LEFT(0) OR RIGHT(0) THEN\n    FRAME=34+(TICK/4) MOD 3\n  ELSE\n    FRAME=32+(TICK/16) MOD 2\n  END IF\n\n  SPRITE 0,PX,PY,FRAME\n  \n  'MONSTER UPDATE\n  IF TICK MOD 40=0 THEN\n    MY=MY+1\n    SPRITE.A 1,(,0)\n  END IF\n  IF TICK MOD 40=20 THEN SPRITE.A 1,(,1)\n  SPRITE 1,MX,MY,12\n\n  WAIT 1\nLOOP\n\n\n#1:MAIN PALETTES\n182D0424003D3824003F2A14003F1B06\n00242A150034202A003E3414003F2A15\n\n#2:MAIN CHARACTERS\n00000000000000000000000000000000\n00000000000000000808400200202101\n030F1E1F3B3F2E05000001000400113A\nC0E0C060E8C090400010389C143C6CBC\n4200210008004200FDFFDFFFFFFFFFFF\nBBBBBBBBBBBBBBBB4444444444444444\nBBBFA7BFFFFFFFBB447C7C7C7E7E7E44\n00000000000000000000000000000000\n001870E0C141861C3C668F1F3FBFFE1C\n1E2359D0B0A00141FFFFE7EFCFDFFFBF\n0098F4E603C1F010FFFFEFDFFFFFFFFF\n00000000000000000000000000000000\n00000000000000000000000000000000\n0000000000000703000003070F0F1F1D\n00000000000070600000E0F0F8F8FCDC\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000001408800200A04404\n1200010003070F072D3F1E0F03070F07\n80000000C0E0F0E07CFCF8F0C0E0F0E0\nFE8181818181817F017F7F7F7F7F7FFF\n11FF22FF88FF22FFFFFFFFFFFFFFFFFF\n7FFF7EFFFEFF7EFFFF818181B1A18181\n11FF7EFFFEFF22FFFF81BDBD81FFFFFF\n00400060000401000020F06000020F00\n02325E4EA4C08080FFFFFFFFDFBFFFFF\nD88C0486030301013F7FFF7FFFFFFFFF\n00000000000000000000000000000000\n00000000000103020000000107060C0D\n00000B09048301000F0F0FFEFF7FFFFF\n0000E84810E0C000F8F8F8BFFFFFFFFF\n000000000000804000000080E0E070B0\n30300000007C286C3000387C7C38286C\n18180000007C28681800387C7C382868\n1818000004384A641800183838384A64\n18180000107850181800183828785018\n18180000103C283018001838283C2830\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000202000000000F0F0F0F07070F0F\n0000202008078080FFFFFFFFBFBF9F9F\n000002020A700101FFFFFFFFFFFDFDF9\n0000404000000000F0F0F0F0F0F0F0F0\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000000000000000000000000000\n00000000040503000F0F0F1F1F1F1F0E\n00040203023240523F7F7F7F3E3E7E2E\n002040C0440E014AFCFEFEFE7C7E7F74\n0000004040A0A040E0F0F0F8F8FCF870\n\n#3:MAIN BG\n00001410000000000000000018020000\n02200320010001000000080200001802\n00000000000000000220032018020000\n02200320010001001200130001000100\n11000100000001000100000000000000\n12001300000008021200130011000100\n00000100000001000100010001000100\n02200320000008020100000000000100\n00000000000018020000010000000000\n00000100000000001200130000000000\n01000000180200000100000008020000\n00000000000001000000010001000100\n01000100110000000100180200000220\n03200000010011000000000000000000\n01000100000001000100110001000100\n00000000000012001300000000000100\n01000000000001000000000000000000\n00000000010001000100000000000000\n00000100000000000000010000000100\n00000000000000000000010000000100\n00000000010001000000000001000000\n01000000000000000000000011000000\n00000100010000000000180218020100\n01000000000000000000000000000100\n01000000000000000000010001000000\n00000000000000000100010001000000\n00000000000000000100010001000100\n00000000010011000802000008020000\n01000100110001000000000000000000\n00000000010011000100180200000100\n01000000000018020000000001000100\n01000000000000000100000000000100\n08020100000002200320010000000100\n01000220032000000100180200000000\n01000100000018020000000000001200\n13000100000002200320120013000000\n00000100010000000000010001000000\n02200320010001000100000000001200\n13001100010018020000000001000802\n18020000000001001200130000000100\n01001802\n\n"
  },
  {
    "path": "programs test/Subs 1.nx",
    "content": "CALL HELLO\nCALL GREET(\"LOWRES\",3)\n\nNUM=10\nCALL INCR(NUM,10)\nPRINT NUM\n\nDIM ARR(10)\n\nARR(2)=5\nCALL INCR(ARR(2),5)\nPRINT ARR(2)\n\n\nSUB HELLO\n PRINT \"HELLO\"\nEND SUB\n\nSUB GREET(NAME$,COUNT)\n FOR I=1 TO COUNT\n  PRINT \"HI\",NAME$\n NEXT I\nEND SUB\n\nSUB INCR(X,Y)\n X=X+Y\nEND SUB\n"
  },
  {
    "path": "programs test/Subs 2.nx",
    "content": "DIM GLOBAL ARR(7)\n\nCALL RANDOM(ARR())\nCALL PRINTARR\n\n\nSUB RANDOM(X())\n FOR I=0 TO 7\n  X(I)=INT(RND*100)\n NEXT I\nEND SUB\n\nSUB PRINTARR\n FOR I=0 TO 7\n  PRINT ARR(I)\n NEXT I\nEND SUB\n"
  },
  {
    "path": "programs test/drawing.nx",
    "content": "REM ** FILL DRAWING AREA WITH CHARACTERS\nI=8\nATTR (0,0,0,0,0)\nFOR CY=0 TO 8\n FOR CX=0 TO 19\n  CELL CX,CY+3,I\n  I=I+1\n NEXT CX\nNEXT CY\n\nREM ** SOME TEXT\nTEXT 0,0, \"     DRAWING WITH TILES?\"\nTEXT 0,15,\"                     NO PROBLEM!\"\n\nREM ** ANIMATE!\nGLOBAL RY,I\nI=0\nON RASTER CALL RASTFX\nDO\n FILL $8080,2880,0\n\n CALL DRAWSINE(40+SIN(I*0.02)*20,0)\n CALL DRAWSINE(30+SIN(I*0.03)*10,1)\n\n I=I+1\n RY=INT(48-SIN(I*0.04)*48)\n WAIT VBL\nLOOP\n\nSUB RASTFX\n REM ** SCROLL TOP AND BOTTOM TEXT LINE\n IF RASTER<8 OR RASTER>=120 THEN SCROLL 0,I,0 ELSE SCROLL 0,0,0\n REM ** PALETTE FX\n IF RASTER=0 OR RASTER=RY+33 THEN PALETTE 0,%000001,%110000,%001100,%110100\n IF RASTER=RY THEN PALETTE 0,%000010,%001111,%110011,%111100\nEND SUB\n\nSUB DRAWSINE(DIV,BP)\n FOR X=0 TO 159\n  Y=36+SIN(X*PI/DIV)*35\n  REM ** GET CHARACTER\n  CX=INT(X/8)\n  CY=INT(Y/8)\n  CI=CY*20+CX\n  REM ** GET BIT\n  BI=2^(7-(X MOD 8))\n  REM ** WRITE IN CHARACTER RAM\n  A=$8080+CI*16+(Y MOD 8)+BP*8\n  POKE A,PEEK(A) OR BI\n NEXT X\nEND SUB\n"
  },
  {
    "path": "programs test/gamepad.nx",
    "content": "REM INIT SPRITES\nSPRITE.A 0,(6,,,,1)\nSPRITE.A 1,(1,,,,1)\n\nDIM X(1),Y(1)\nX(0)=60\nY(0)=64\nX(1)=100\nY(1)=64\n\nGAMEPAD 2\n\nDO\n  FOR I=0 TO 1\n    SPRITE I,,,130\n    IF UP(I) THEN Y(I)=Y(I)-1\n    IF DOWN(I) THEN Y(I)=Y(I)+1\n    IF LEFT(I) THEN X(I)=X(I)-1\n    IF RIGHT(I) THEN X(I)=X(I)+1\n    IF BUTTON(I,0) THEN SPRITE I,,,132\n    IF BUTTON(I,1) THEN SPRITE I,,,134\n    SPRITE I,X(I)+24,Y(I)+24,\n  NEXT I\n  WAIT VBL\nLOOP\n\n\n#1:MAIN PALETTES\n07 10 24 38 00 04 08 0C 00 07 0B 0F 00 1B 2F 3F\n00 34 38 3C 00 10 20 30 00 10 30 39 00 3F 00 00\n\n#2:MAIN CHARACTERS\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 20 04 00 00 44 00 02 FF DF FF FF FF FB FF FF \nFF FF FF 77 22 00 88 DD FF FF FF FF FF FF 77 22 \nC0 E0 E8 E8 E2 C8 E0 C2 FF FF FF FF FF FF FF FF \n01 09 4B 0B 03 0B 01 23 FE F6 B4 F4 FC F4 FE DC \n03 0C 12 20 40 44 80 80 03 0F 1D 3F 7F 7B FF FF \n80 82 40 48 20 10 0C 03 7F 7D 3F 37 1F 0F 03 00 \n00 02 00 00 10 00 00 00 FF FD FF FF EF FF FF FF \n00 83 44 38 00 83 44 38 FF 7C BB C7 FF 7C BB C7 \n00 83 44 38 00 83 44 38 00 83 C7 FF FF 7C BB C7 \n08 08 08 08 08 8B 44 28 10 10 10 10 D3 74 38 10 \n08 08 08 08 08 08 08 08 10 10 12 14 10 50 30 10 \n30 38 27 43 C2 E4 1C 18 30 38 3F 7F FE FC 1C 18 \n00 00 00 00 00 10 44 55 00 00 00 00 00 10 44 55 \n7E 81 A3 81 89 85 A1 7E 7E FE FE FE FE FA DE 00 \n10 22 0C 38 70 C4 08 02 EF DF FF FF FF FB F7 FF \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n03 0C 10 20 40 40 80 80 03 0F 1F 3F 7F 7F FF FF \n80 80 40 40 20 10 0C 03 7F 7F 3F 3F 1F 0F 03 00 \n00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF \n00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 \n03 04 04 18 20 20 40 80 03 07 07 1F 3F 3F 7F FF \n80 40 30 08 04 02 02 01 00 80 C0 F0 F8 FC FC FE \n00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF \n00 73 00 E6 00 70 02 38 FF FC FF 1F FF FF FF C7 \n00 80 18 00 01 0C 20 00 FF 7F FF FF FF F3 FF FF \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n03 07 07 0F 1F 1F 07 05 00 00 01 04 07 07 03 02 \nE0 F8 F0 FC FC F8 F0 C0 00 00 B0 FC DC 80 F0 20 \n00 03 07 07 0F 1F 1F 07 00 00 00 01 04 07 07 03 \n00 E0 F8 F0 FC FC F8 F0 00 00 00 B0 FC DC 80 F0 \n00 03 07 07 0F 1F 1F 07 00 00 00 01 04 07 07 03 \n00 E0 F8 F0 FC FC F8 F0 00 00 00 B0 FC DC 80 F0 \n07 0F 0F 1F 3F 3F 0F 1F 00 00 03 09 0F 0F 07 00 \nC0 F0 E0 F8 F8 F0 E0 40 00 00 60 F8 B8 00 E0 80 \n00 07 0F 0F 1F 3F 3F 0F 00 00 00 03 09 0F 0F 03 \n0E DE FE FE FE FC FC F8 0E 16 0E 60 F8 B8 00 E0 \n00 00 00 00 00 03 07 07 00 00 00 00 00 00 00 01 \n00 00 00 00 00 E0 F8 F0 00 00 00 00 00 00 00 B0 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n0D 1D 1E 1C 0C 00 0E 1E 02 02 03 1F 0F 0F 00 00 \nD0 D8 3C 1C 18 00 38 3C 20 20 E0 FC F8 78 00 00 \n0F 1F 38 30 18 18 18 0C 00 00 3F 37 07 07 00 00 \n5C 5C 98 38 38 38 00 00 BC BC F0 C0 C0 80 00 00 \n0B 13 03 30 30 30 20 00 04 0C 1C 0F 0F 0C 00 00 \n00 D8 FC FC 80 00 70 78 C0 18 1C 1C 60 E0 00 00 \n17 07 00 00 01 05 0E 07 08 18 1F 0F 0E 02 00 00 \nC0 E0 60 00 80 C0 40 00 40 60 E0 E0 60 00 00 00 \n3E 3E 3C 1C 66 63 60 40 01 09 1F 1B 19 1C 1E 00 \nB0 A0 82 06 06 86 00 00 40 50 F0 F8 F8 78 00 00 \n0F 0F 0F 1B 1F 1C 0E 1E 04 07 03 07 06 0F 01 00 \nFC FC F8 FC FC 0C 1C 1E FC DC 80 F0 28 FC E0 00 \n"
  },
  {
    "path": "programs test/hello world.nx",
    "content": "PRINT \"HELLO WORLD\"\n\nGAMEPAD 1\n\nBG 1\nBG COPY 0,0,32,32 TO 0,0\n\nX=32\nY=32\nDO\n  BGX=BGX+1\n  SCROLL 1,BGX,0\n  IF UP(0) THEN Y=Y-1\n  IF DOWN(0) THEN Y=Y+1\n  IF LEFT(0) THEN X=X-1\n  IF RIGHT(0) THEN X=X+1\n  SPRITE 0,X,Y,TIMER/8 MOD 2 + 1\n  WAIT VBL\nLOOP\n\n#1:MAIN PALETTES\n05 3F 2F 00 00 38 34 00 00 0C 24 10 00 3F 2A 15\n00 00 00 00 00 00 00 00 00 00 00 00 00 3F 2A 15\n\n#2:MAIN CHARACTERS\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n18 18 81 00 00 18 18 3C 18 00 7E 18 18 18 18 3C\n18 18 00 00 00 99 18 3C 18 00 18 3C 5A 18 18 3C\nFF FF 5E 04 00 44 FF FF 00 00 A1 FB FF FF FF FF\n3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C 3C\n3C 7E FF FF FF FF FE 7C 00 00 00 00 00 00 00 00\n10 08 08 88 49 2E 1C 1C 00 00 00 00 00 00 00 00\n\n#3:MAIN BG\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 05 02 00 00 00 00 05 02 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 02\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 03 02 03 02 03 02 03 02 03 02 03 02 00 00\n00 00 00 00 00 00 00 00 05 22 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 03 02 03 02\n03 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 03 22 03 22 03 22 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 22\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 05 02 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 05 02 00 00 06 12 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 22\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 05 02 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 03 02 03 02 03 02 03 02 03 02\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 22\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 04 02 06 02 00 00 00 00 00 00 00 00 00 00\n05 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 03 22 03 22 03 22\n03 22 00 00 00 00 00 00 00 00 00 00 05 22 00 00\n03 02 03 02 03 02 03 02 00 00 00 00 00 00 00 00\n05 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 05 22 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n05 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 05 02 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 05 22 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 06 2A 00 00\n04 23 00 00 06 2A 00 00 00 00 00 00 00 00 06 12\n00 00 04 02 00 00 06 1A 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 04 22 00 00\n00 00 00 00 00 00 00 00 00 00 03 22 03 22 03 22\n03 22 03 22 03 22 03 22 00 00 00 00 00 00 03 02\n03 02 03 02 03 02 03 02 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 03 22 03 22 03 22\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 03\n03 03 03 03 03 03 00 00 00 00 00 00 00 00 00 00\n00 00 03 03 03 03 03 03 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n\n"
  },
  {
    "path": "programs test/sprite collision.nx",
    "content": "PALETTE 1,0,4,8,16\n\nREM INIT SPRITES\nSPRITE.A 0,(0,1,0,,3)\nSPRITE.A 1,(1,0,1,,3)\nSPRITE.A 2,(1,0,0,,0)\nSPRITE.A 3,(1,0,0,,0)\n\nDIM X(1),Y(1)\nX=42\nY=32\nSPRITE 0,X,Y,3\nSPRITE 1,88,48,3\nSPRITE 2,18,38,1\nSPRITE 3,128,98,2\n\nGAMEPAD 1\n\nDO\n  IF UP(0) THEN Y=Y-1\n  IF DOWN(0) THEN Y=Y+1\n  IF LEFT(0) THEN X=X-1\n  IF RIGHT(0) THEN X=X+1\n  SPRITE 0,X,Y,\n  IF SPRITE HIT(0) THEN TEXT 0,0,\"HIT \"+STR$(HIT) ELSE TEXT 0,0,\"      \"\n  WAIT VBL\nLOOP\n\n\n#2:MAIN CHARACTERS\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n08 1C 3E FF 00 60 60 60 08 1C 3E 00 FF 60 60 60\nD8 08 00 08 08 09 00 01 E0 00 08 08 08 0E 01 01\n80 60 38 1F 0B 09 08 08 00 00 00 00 04 06 07 06\n00 00 00 81 C3 77 7C 00 00 00 00 00 00 80 83 FF\n00 00 F0 00 00 00 00 00 00 00 00 FF FF 81 01 00\n00 00 00 00 00 00 00 00 00 00 00 00 C0 E0 F8 7C\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n10 10 18 0E 02 03 01 01 0C 0C 06 01 01 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 80 80 C0\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 01 02 1E 0F 07 03 03 03 03 02\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n01 03 06 38 20 20 60 40 00 00 01 07 1C 10 10 20\n00 00 00 00 00 00 00 00 C0 80 00 00 00 00 00 00\n00 00 00 00 00 03 07 06 00 00 00 00 00 03 07 06\n06 0E 1C 3C F8 F8 F0 00 06 0E 1C 3C F8 F8 F0 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 60 60 30 38 1F 07 00 00\n00 00 00 00 00 00 00 00 00 00 00 C0 E0 F0 1C 0F\n06 0E 1C 1C 1C 38 F0 E0 06 0E 1C 1C 1C 38 F0 E0\n"
  },
  {
    "path": "programs test/touch.nx",
    "content": "REM INIT SPRITE\nFOR I=0 TO 15\n  SPRITE I,-32,-32,130\n  SPRITE.A I,(INT(I/2),,,,1)\nNEXT I\n\nDO\n  FOR I=15 TO 1 STEP -1\n    SPRITE I,SPRITE.X(I-1),SPRITE.Y(I-1),\n  NEXT I\n  IF TOUCH THEN\n    TEXT 0,0,\"DRAG!\"\n    SPRITE 0,TOUCH.X-8,TOUCH.Y-8,\n  ELSE\n    TEXT 0,0,\"TOUCH\"\n    SPRITE OFF 0\n  END IF\n  WAIT VBL\nLOOP\n\n\n#1: MAIN PALETTES\n07 10 24 38 00 04 08 0C 00 07 0B 0F 00 1B 2F 3F\n00 34 38 3C 00 10 20 30 00 10 30 39 00 3F 00 00\n\n#2: MAIN CHARACTERS\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 20 04 00 00 44 00 02 FF DF FF FF FF FB FF FF \nFF FF FF 77 22 00 88 DD FF FF FF FF FF FF 77 22 \nC0 E0 E8 E8 E2 C8 E0 C2 FF FF FF FF FF FF FF FF \n01 09 4B 0B 03 0B 01 23 FE F6 B4 F4 FC F4 FE DC \n03 0C 12 20 40 44 80 80 03 0F 1D 3F 7F 7B FF FF \n80 82 40 48 20 10 0C 03 7F 7D 3F 37 1F 0F 03 00 \n00 02 00 00 10 00 00 00 FF FD FF FF EF FF FF FF \n00 83 44 38 00 83 44 38 FF 7C BB C7 FF 7C BB C7 \n00 83 44 38 00 83 44 38 00 83 C7 FF FF 7C BB C7 \n08 08 08 08 08 8B 44 28 10 10 10 10 D3 74 38 10 \n08 08 08 08 08 08 08 08 10 10 12 14 10 50 30 10 \n30 38 27 43 C2 E4 1C 18 30 38 3F 7F FE FC 1C 18 \n00 00 00 00 00 10 44 55 00 00 00 00 00 10 44 55 \n7E 81 A3 81 89 85 A1 7E 7E FE FE FE FE FA DE 00 \n10 22 0C 38 70 C4 08 02 EF DF FF FF FF FB F7 FF \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n03 0C 10 20 40 40 80 80 03 0F 1F 3F 7F 7F FF FF \n80 80 40 40 20 10 0C 03 7F 7F 3F 3F 1F 0F 03 00 \n00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF \n00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 \n03 04 04 18 20 20 40 80 03 07 07 1F 3F 3F 7F FF \n80 40 30 08 04 02 02 01 00 80 C0 F0 F8 FC FC FE \n00 00 00 00 00 00 00 00 FF FF FF FF FF FF FF FF \n00 73 00 E6 00 70 02 38 FF FC FF 1F FF FF FF C7 \n00 80 18 00 01 0C 20 00 FF 7F FF FF FF F3 FF FF \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n03 07 07 0F 1F 1F 07 05 00 00 01 04 07 07 03 02 \nE0 F8 F0 FC FC F8 F0 C0 00 00 B0 FC DC 80 F0 20 \n00 03 07 07 0F 1F 1F 07 00 00 00 01 04 07 07 03 \n00 E0 F8 F0 FC FC F8 F0 00 00 00 B0 FC DC 80 F0 \n00 03 07 07 0F 1F 1F 07 00 00 00 01 04 07 07 03 \n00 E0 F8 F0 FC FC F8 F0 00 00 00 B0 FC DC 80 F0 \n07 0F 0F 1F 3F 3F 0F 1F 00 00 03 09 0F 0F 07 00 \nC0 F0 E0 F8 F8 F0 E0 40 00 00 60 F8 B8 00 E0 80 \n00 07 0F 0F 1F 3F 3F 0F 00 00 00 03 09 0F 0F 03 \n0E DE FE FE FE FC FC F8 0E 16 0E 60 F8 B8 00 E0 \n00 00 00 00 00 03 07 07 00 00 00 00 00 00 00 01 \n00 00 00 00 00 E0 F8 F0 00 00 00 00 00 00 00 B0 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n0D 1D 1E 1C 0C 00 0E 1E 02 02 03 1F 0F 0F 00 00 \nD0 D8 3C 1C 18 00 38 3C 20 20 E0 FC F8 78 00 00 \n0F 1F 38 30 18 18 18 0C 00 00 3F 37 07 07 00 00 \n5C 5C 98 38 38 38 00 00 BC BC F0 C0 C0 80 00 00 \n0B 13 03 30 30 30 20 00 04 0C 1C 0F 0F 0C 00 00 \n00 D8 FC FC 80 00 70 78 C0 18 1C 1C 60 E0 00 00 \n17 07 00 00 01 05 0E 07 08 18 1F 0F 0E 02 00 00 \nC0 E0 60 00 80 C0 40 00 40 60 E0 E0 60 00 00 00 \n3E 3E 3C 1C 66 63 60 40 01 09 1F 1B 19 1C 1E 00 \nB0 A0 82 06 06 86 00 00 40 50 F0 F8 F8 78 00 00 \n0F 0F 0F 1B 1F 1C 0E 1E 04 07 03 07 06 0F 01 00 \nFC FC F8 FC FC 0C 1C 1E FC DC 80 F0 28 FC E0 00 \n"
  },
  {
    "path": "scripts/export_characters.py",
    "content": "import sys\nfrom PIL import Image\n\nif len(sys.argv) >= 2:\n\tfilename = sys.argv[1]\nelse:\n\tfilename = \"../assets/characters.png\"\nim = Image.open(filename)\nprint im.format, im.size, im.mode\nprint \"{\",\nfor row in range(16):\n\tfor column in range(16):\n\t\tprint \"{\",\n\t\tfor bit in range(2):\n\t\t\tfor charY in range(8):\n\t\t\t\ty = row*8+charY\n\t\t\t\tval = 0\n\t\t\t\tfor charX in range(8):\n\t\t\t\t\tpixel = im.getpixel((column*8+charX, y))[0] / 64\n\t\t\t\t\tpcolor = 0\n\t\t\t\t\tif pixel > 0:\n\t\t\t\t\t\tpcolor = 4 - pixel\n\t\t\t\t\tpbit = (pcolor >> bit) & 0x01\n\t\t\t\t\tval |= (pbit << (7-charX))\n\t\t\t\tprint str(val & 0xFF)+ \",\",\n\t\tprint \"},\"\nprint \"}\""
  },
  {
    "path": "scripts/export_characters_hex.py",
    "content": "import sys\nfrom PIL import Image\n\nif len(sys.argv) >= 2:\n\tfilename = sys.argv[1]\nelse:\n\tfilename = \"../assets/characters.png\"\nim = Image.open(filename)\nprint im.format, im.size, im.mode\nfor row in range(16):\n\tfor column in range(16):\n\t\tfor bit in range(2):\n\t\t\tfor charY in range(8):\n\t\t\t\ty = row*8+charY\n\t\t\t\tval = 0\n\t\t\t\tfor charX in range(8):\n\t\t\t\t\tpixel = im.getpixel((column*8+charX, y))[0] / 64\n\t\t\t\t\tpcolor = 0\n\t\t\t\t\tif pixel > 0:\n\t\t\t\t\t\tpcolor = 4 - pixel\n\t\t\t\t\tpbit = (pcolor >> bit) & 0x01\n\t\t\t\t\tval |= (pbit << (7-charX))\n\t\t\t\tprint \"%0.2X\" % (val & 0xFF),\n\t\tprint \"\"\n"
  },
  {
    "path": "sdl/config.h",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef config_h\n#define config_h\n\n#ifdef __EMSCRIPTEN__\n#define DEV_MENU 0\n#define SCREENSHOTS 0\n#define HOT_KEYS 0\n#define SETTINGS_FILE 0\n#else\n#define DEV_MENU 1\n#define SCREENSHOTS 1\n#define HOT_KEYS 1\n#define SETTINGS_FILE 1\n#endif\n\n#endif /* config_h */\n"
  },
  {
    "path": "sdl/dev_menu.c",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"config.h\"\n\n#if DEV_MENU\n\n#include \"dev_menu.h\"\n#include \"main.h\"\n#include \"dev_menu_data.h\"\n#include \"text_lib.h\"\n#include \"string_utils.h\"\n#include \"system_paths.h\"\n#include \"utils.h\"\n#include \"sdl_include.h\"\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\n#define MENU_SIZE 5\n\nstruct DevButton {\n    int cx;\n    int cy;\n};\n\nstruct DevButton devButtons[] = {\n    {1,4},\n    {3,4},\n    {5,4},\n    {7,4},\n    {9,4},\n    {17,4}\n};\n\nvoid dev_showInfo(struct DevMenu *devMenu);\nvoid dev_showError(struct DevMenu *devMenu, struct CoreError error);\nvoid dev_updateButtons(struct DevMenu *devMenu);\nvoid dev_onButtonTap(struct DevMenu *devMenu);\nvoid dev_showToolsMenu(struct DevMenu *devMenu);\nvoid dev_showClearRamMenu(struct DevMenu *devMenu);\nvoid dev_showMenu(struct DevMenu *devMenu, const char *message, const char *buttons[], int numButtons, int numRemoveButtons);\nvoid dev_clearPersistentRam(struct DevMenu *devMenu);\n\nvoid dev_init(struct DevMenu *devMenu, struct Runner *runner, struct Settings *settings)\n{\n    memset(devMenu, 0, sizeof(struct DevMenu));\n    devMenu->runner = runner;\n    devMenu->settings = settings;\n}\n\nvoid dev_show(struct DevMenu *devMenu, bool reload)\n{\n    if (reload)\n    {\n        devMenu->lastError = runner_loadProgram(devMenu->runner, getMainProgramFilename());\n    }\n    \n    devMenu->currentMenu = DevModeMenuMain;\n    devMenu->currentButton = -1;\n    devMenu->lastTouch = false;\n    \n    struct Core *core = devMenu->runner->core;\n    \n    struct TextLib *textLib = &devMenu->textLib;\n    textLib->core = core;\n    \n    itp_endProgram(core);\n    machine_reset(core, true);\n    overlay_reset(core);\n    \n    core->machine->ioRegisters.attr.touchEnabled = 1;\n    core->machineInternals->isEnergySaving = true;\n    \n    txtlib_clearScreen(textLib);\n    textLib->fontCharOffset = 192;\n    textLib->windowY = 7;\n    textLib->windowHeight = 9;\n    \n    memcpy(&core->machine->colorRegisters, dev_colors, sizeof(dev_colors));\n    memcpy(&core->machine->videoRam.characters, dev_characters, sizeof(dev_characters));\n    memcpy(&core->machine->cartridgeRom, dev_bg, sizeof(dev_bg));\n    \n    textLib->sourceAddress = 4;\n    textLib->sourceWidth = core->machine->cartridgeRom[2];\n    \n    txtlib_copyBackground(textLib, 0, 0, 20, 16, 0, 0);\n    dev_updateButtons(devMenu);\n    \n    textLib->charAttr.palette = 1;\n    txtlib_writeText(textLib, \"DEVELOPMENT MENU\", 2, 0);\n    \n    textLib->charAttr.palette = 0;\n    char progName[19];\n    displayName(getMainProgramFilename(), progName, 19);\n    txtlib_writeText(textLib, progName, 1, 2);\n    \n    if (devMenu->lastError.code != ErrorNone)\n    {\n        dev_showError(devMenu, devMenu->lastError);\n    }\n    else\n    {\n        dev_showInfo(devMenu);\n    }\n    \n    setMouseEnabled(true);\n}\n\nvoid dev_update(struct DevMenu *devMenu, struct CoreInput *input)\n{\n    struct Core *core = devMenu->runner->core;\n    struct TextLib *textLib = &devMenu->textLib;\n    \n    core_handleInput(core, input);\n    \n    bool touch = core->machine->ioRegisters.status.touch;\n    int cx = core->machine->ioRegisters.touchX / 8;\n    int cy = core->machine->ioRegisters.touchY / 8;\n    \n    if (devMenu->currentMenu == DevModeMenuMain)\n    {\n        if (devMenu->currentButton >= 0)\n        {\n            int bcx = devButtons[devMenu->currentButton].cx;\n            int bcy = devButtons[devMenu->currentButton].cy;\n            bool isInside = (cx >= bcx && cy >= bcy && cx <= bcx + 1 && cy <= bcy + 1);\n            if (!touch || !isInside)\n            {\n                txtlib_setCellsAttr(textLib, bcx, bcy, bcx + 1, bcy + 1, 0, -1, -1, -1);\n                \n                if (isInside)\n                {\n                    dev_onButtonTap(devMenu);\n                }\n                devMenu->currentButton = -1;\n            }\n        }\n        else if (touch && !devMenu->lastTouch)\n        {\n            for (int i = 0; i < 6; i++)\n            {\n                int bcx = devButtons[i].cx;\n                int bcy = devButtons[i].cy;\n                if (cx >= bcx && cy >= bcy && cx <= bcx + 1 && cy <= bcy + 1)\n                {\n                    txtlib_setCellsAttr(textLib, bcx, bcy, bcx + 1, bcy + 1, 1, -1, -1, -1);\n                    devMenu->currentButton = i;\n                }\n            }\n        }\n    }\n    else\n    {\n        if (devMenu->currentButton >= 0)\n        {\n            int bcy = 1 + devMenu->currentButton * 3;\n            bool isInside = (cy >= bcy && cy <= bcy + 2);\n            if (!touch || !isInside)\n            {\n                txtlib_setCellsAttr(textLib, 0, bcy, 19, bcy + 2, 0, -1, -1, -1);\n                \n                if (isInside)\n                {\n                    dev_onButtonTap(devMenu);\n                }\n                devMenu->currentButton = -1;\n            }\n        }\n        else if (touch && !devMenu->lastTouch)\n        {\n            int button = (cy - 1) / 3;\n            if (button >= 0 && button < devMenu->currentMenuSize)\n            {\n                int bcy = 1 + button * 3;\n                txtlib_setCellsAttr(textLib, 0, bcy, 19, bcy + 2, 1, -1, -1, -1);\n                devMenu->currentButton = button;\n            }\n        }\n    }\n    devMenu->lastTouch = core->machine->ioRegisters.status.touch;\n    \n    overlay_draw(core, false);\n}\n\nbool dev_handleDropFile(struct DevMenu *devMenu, const char *filename)\n{\n    if (devMenu->currentMenu == DevModeMenuTools)\n    {\n        if (settings_addTool(devMenu->settings, filename))\n        {\n            settings_save(devMenu->settings);\n            dev_showToolsMenu(devMenu);\n        }\n        else\n        {\n            overlay_message(devMenu->runner->core, \"NO EMPTY SPACE\");\n        }\n        return true;\n    }\n    return false;\n}\n\nvoid dev_showInfo(struct DevMenu *devMenu)\n{\n    struct Core *core = devMenu->runner->core;\n    struct TextLib *textLib = &devMenu->textLib;\n    \n    char info[21];\n    \n    textLib->charAttr.palette = 5;\n    txtlib_writeText(textLib, \"TOKENS:\", 0, 7);\n    txtlib_writeText(textLib, \"ROM:\", 0, 8);\n\n    textLib->charAttr.palette = 0;\n    sprintf(info, \"%d/%d\", core->interpreter->tokenizer.numTokens, MAX_TOKENS);\n    txtlib_writeText(textLib, info, 20 - (int)strlen(info), 7);\n    sprintf(info, \"%d/%d\", data_currentSize(&core->interpreter->romDataManager), DATA_SIZE);\n    txtlib_writeText(textLib, info, 20 - (int)strlen(info), 8);\n    \n    textLib->charAttr.palette = 4;\n    txtlib_writeText(textLib, \"READY TO RUN\", 4, 14);\n}\n\nvoid dev_showError(struct DevMenu *devMenu, struct CoreError error)\n{\n    struct Core *core = devMenu->runner->core;\n    struct TextLib *textLib = &devMenu->textLib;\n    \n    textLib->charAttr.palette = 0;\n    \n    txtlib_clearWindow(textLib);\n    \n    textLib->charAttr.palette = 2;\n    txtlib_printText(textLib, err_getString(error.code));\n    txtlib_printText(textLib, \"\\n\");\n    if (error.sourcePosition >= 0 && core->interpreter->sourceCode)\n    {\n        textLib->charAttr.palette = 0;\n        int number = lineNumber(core->interpreter->sourceCode, error.sourcePosition);\n        char lineNumberText[30];\n        sprintf(lineNumberText, \"IN LINE %d:\\n\", number);\n        txtlib_printText(textLib, lineNumberText);\n        \n        const char *line = lineString(core->interpreter->sourceCode, error.sourcePosition);\n        if (line)\n        {\n            textLib->charAttr.palette = 5;\n            txtlib_printText(textLib, \"\\n\");\n            txtlib_printText(textLib, line);\n            free((void *)line);\n        }\n    }\n}\n\nvoid dev_updateButtons(struct DevMenu *devMenu)\n{\n    struct Plane *bg = &devMenu->runner->core->machine->videoRam.planeA;\n    if (devMenu->runner->core->interpreter->debug)\n    {\n        bg->cells[5][7].character = 30;\n        bg->cells[5][8].character = 31;\n    }\n    else\n    {\n        bg->cells[5][7].character = 46;\n        bg->cells[5][8].character = 47;\n    }\n}\n\nvoid dev_onButtonTap(struct DevMenu *devMenu)\n{\n    int button = devMenu->currentButton;\n    \n    if (devMenu->currentMenu == DevModeMenuMain)\n    {\n        if (button == 0)\n        {\n            // Run\n            runMainProgram();\n        }\n        else if (button == 1)\n        {\n            // Check\n            dev_show(devMenu, true);\n        }\n        else if (button == 2)\n        {\n            dev_showToolsMenu(devMenu);\n        }\n        else if (button == 3)\n        {\n            // Debug On/Off\n            devMenu->runner->core->interpreter->debug = !devMenu->runner->core->interpreter->debug;\n            dev_updateButtons(devMenu);\n        }\n        else if (button == 4)\n        {\n            dev_showClearRamMenu(devMenu);\n        }\n        else if (button == 5)\n        {\n            // Eject\n            rebootNX();\n        }\n    }\n    else if (devMenu->currentMenu == DevModeMenuTools)\n    {\n        if (devMenu->currentButton < devMenu->settings->numTools)\n        {\n            int cx = devMenu->runner->core->machine->ioRegisters.touchX / 8;\n            if (cx >= 18)\n            {\n                settings_removeTool(devMenu->settings, devMenu->currentButton);\n                settings_save(devMenu->settings);\n                dev_showToolsMenu(devMenu);\n            }\n            else\n            {\n                runToolProgram(devMenu->settings->tools[devMenu->currentButton]);\n            }\n        }\n        else\n        {\n            dev_show(devMenu, false);\n        }\n    }\n    else if (devMenu->currentMenu == DevModeMenuClearRam)\n    {\n        if (devMenu->currentButton == 0)\n        {\n            dev_clearPersistentRam(devMenu);\n        }\n        dev_show(devMenu, false);\n    }\n}\n\nvoid dev_showToolsMenu(struct DevMenu *devMenu)\n{\n    struct TextLib *textLib = &devMenu->textLib;\n    \n    devMenu->currentMenu = DevModeMenuTools;\n    const char *menu[MENU_SIZE];\n    int count = 0;\n    for (int i = 0; i < devMenu->settings->numTools; i++)\n    {\n        menu[count++] = devMenu->settings->toolNames[i];\n    }\n    menu[count++] = \"CANCEL\";\n    dev_showMenu(devMenu, \"EDIT ROM WITH TOOL\", menu, count, count - 1);\n    if (count < MENU_SIZE)\n    {\n        textLib->charAttr.palette = 5;\n        txtlib_writeText(textLib, \"DRAG & DROP PROGRAM\", 0, 14);\n        txtlib_writeText(textLib, \"TO ADD AS TOOL\", 3, 15);\n    }\n}\n\nvoid dev_showClearRamMenu(struct DevMenu *devMenu)\n{\n    struct TextLib *textLib = &devMenu->textLib;\n    \n    devMenu->currentMenu = DevModeMenuClearRam;\n    const char *menu[MENU_SIZE];\n    menu[0] = \"CLEAR\";\n    menu[1] = \"CANCEL\";\n    dev_showMenu(devMenu, \"CLEAR PERSIST. RAM?\", menu, 2, 0);\n    \n    textLib->charAttr.palette = 5;\n    txtlib_writeText(textLib, \"MAY DELETE DATA LIKE\", 0, 12);\n    txtlib_writeText(textLib, \"GAME STATE OR\", 3, 13);\n    txtlib_writeText(textLib, \"HIGH SCORES\", 4, 14);\n    txtlib_writeText(textLib, \"OF THIS PROGRAM\", 2, 15);\n}\n\nvoid dev_showMenu(struct DevMenu *devMenu, const char *message, const char *buttons[], int numButtons, int numRemoveButtons)\n{\n    struct TextLib *textLib = &devMenu->textLib;\n    \n    textLib->charAttr.palette = 0;\n    txtlib_setCells(textLib, 0, 0, 19, 15, 1);\n    \n    textLib->charAttr.palette = 1;\n    txtlib_setCells(textLib, 0, 0, 19, 0, 192);\n    txtlib_writeText(textLib, message, (int)(20 - strlen(message))/2, 0);\n    \n    textLib->charAttr.palette = 0;\n    for (int i = 0; i < numButtons; i++)\n    {\n        int y = 1 + i * 3;\n        txtlib_setCells(textLib, 0, y, 19, y, 3);\n        txtlib_setCells(textLib, 0, y + 2, 19, y + 2, 5);\n        int tx = (int)(20 - strlen(buttons[i])) / 2;\n        if (tx < 0) tx = 0;\n        txtlib_writeText(textLib, buttons[i], tx, y + 1);\n        if (i < numRemoveButtons)\n        {\n            txtlib_setCell(textLib, 19, y, 20);\n        }\n    }\n    devMenu->currentMenuSize = numButtons;\n}\n\nvoid dev_clearPersistentRam(struct DevMenu *devMenu)\n{\n    char ramFilename[FILENAME_MAX];\n    getRamFilename(ramFilename);\n    remove(ramFilename);\n}\n\n#endif\n\n"
  },
  {
    "path": "sdl/dev_menu.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef dev_menu_h\n#define dev_menu_h\n\n#include \"config.h\"\n\n#if DEV_MENU\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"core.h\"\n#include \"settings.h\"\n#include \"runner.h\"\n#include \"text_lib.h\"\n\nenum DevModeMenu {\n    DevModeMenuMain,\n    DevModeMenuTools,\n    DevModeMenuClearRam\n};\n\nstruct DevMenu {\n    struct Runner *runner;\n    struct Settings *settings;\n    bool lastTouch;\n    enum DevModeMenu currentMenu;\n    int currentButton;\n    int currentMenuSize;\n    struct CoreError lastError;\n    struct TextLib textLib;\n};\n\nvoid dev_init(struct DevMenu *devMenu, struct Runner *runner, struct Settings *settings);\nvoid dev_show(struct DevMenu *devMenu, bool reload);\nvoid dev_update(struct DevMenu *devMenu, struct CoreInput *input);\nbool dev_handleDropFile(struct DevMenu *devMenu, const char *filename);\n\n#endif\n\n#endif /* dev_menu_h */\n"
  },
  {
    "path": "sdl/dev_menu_data.h",
    "content": "//\n// Copyright 2017 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef dev_menu_data_h\n#define dev_menu_data_h\n\n#include \"config.h\"\n\n#if DEV_MENU\n\nconst char dev_colors[] = {\n    0x05, 0x3F, 0x2A, 0x15, 0x00, 0x0F, 0x05, 0x00, 0x3C, 0x30, 0x2A, 0x00, 0x00, 0x3C, 0x2A, 0x00,\n    0x00, 0x08, 0x2A, 0x15, 0x00, 0x15, 0x2A, 0x15, 0x00, 0x3F, 0x2A, 0x15, 0x00, 0x3F, 0x2A, 0x15,\n};\n\nconst char dev_characters[] = {\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,\n    0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0xFF, 0x80, 0xB2, 0xAA, 0xB2, 0xA9, 0x80, 0x80, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,\n    0xFE, 0x01, 0xB1, 0xA9, 0xA9, 0xA9, 0x01, 0x61, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0xFF, 0x80, 0xBB, 0xB2, 0xA2, 0xBB, 0x80, 0x80, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,\n    0xFE, 0x01, 0x01, 0x81, 0x81, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0xFF, 0x80, 0x9A, 0xA3, 0xA2, 0x9A, 0x80, 0x80, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,\n    0xFE, 0x01, 0xA9, 0xB1, 0xA9, 0xA9, 0x01, 0x0D, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0xFF, 0x80, 0xBB, 0xB1, 0xA1, 0xBA, 0x80, 0x80, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,\n    0xFE, 0x01, 0x65, 0x69, 0x49, 0x65, 0x01, 0x21, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0xFF, 0x80, 0xB3, 0xAB, 0xAA, 0xB3, 0x80, 0x81, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,\n    0xFE, 0x01, 0xB1, 0x31, 0x29, 0xB1, 0x01, 0xF9, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0xFF, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF,\n    0x71, 0x79, 0x7D, 0x79, 0x71, 0x61, 0x01, 0xFF, 0xDF, 0xCF, 0xC7, 0xCF, 0xDF, 0xFF, 0xFF, 0xFF,\n    0x80, 0x9C, 0xBE, 0xBE, 0xBE, 0x94, 0x80, 0x7F, 0x7F, 0x63, 0x55, 0x41, 0x41, 0x6B, 0x7F, 0xFF,\n    0x0D, 0x1D, 0x39, 0x71, 0xE1, 0xC1, 0x01, 0xFF, 0xFF, 0xF7, 0xEF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x80, 0x81, 0x83, 0x81, 0x80, 0x80, 0x80, 0x7F, 0x7F, 0x7F, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF,\n    0x1D, 0x39, 0xB9, 0xF1, 0xF1, 0x61, 0x01, 0xFF, 0xF7, 0xEF, 0xEF, 0x5F, 0x9F, 0xFF, 0xFF, 0xFF,\n    0x80, 0x80, 0x81, 0x81, 0x80, 0x81, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF,\n    0x71, 0xF9, 0xFD, 0xFD, 0x01, 0xFD, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x83, 0x83, 0x83, 0x83, 0x83, 0x81, 0x80, 0x7F, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0xFF,\n    0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xF9, 0x01, 0xFF, 0x07, 0x17, 0xA7, 0x47, 0x07, 0xFF, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0xFF, 0x80, 0x9A, 0xA2, 0xA2, 0x9B, 0x80, 0xFF, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,\n    0xFE, 0x01, 0x31, 0x29, 0x31, 0xA9, 0x01, 0xFD, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x82, 0x82, 0x82, 0x82, 0x82, 0x81, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF,\n    0x05, 0x05, 0x05, 0x05, 0x05, 0xF9, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0xCE, 0xD5, 0xCC, 0xD5, 0xFF, 0xAA, 0x80, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x55, 0x7F, 0xFF,\n    0xC5, 0x45, 0x55, 0x55, 0xFD, 0xA9, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x57, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x00, 0xFF, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF, 0xE7, 0xFF,\n    0x00, 0x6C, 0x6C, 0x24, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x93, 0x93, 0xDB, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x00, 0x24, 0x7E, 0x24, 0x24, 0x7E, 0x24, 0x00, 0xFF, 0xDB, 0x81, 0xDB, 0xDB, 0x81, 0xDB, 0xFF,\n    0x00, 0x08, 0x3E, 0x38, 0x0E, 0x3E, 0x08, 0x00, 0xFF, 0xF7, 0xC1, 0xC7, 0xF1, 0xC1, 0xF7, 0xFF,\n    0x00, 0x62, 0x64, 0x08, 0x10, 0x26, 0x46, 0x00, 0xFF, 0x9D, 0x9B, 0xF7, 0xEF, 0xD9, 0xB9, 0xFF,\n    0x00, 0x1C, 0x34, 0x38, 0x6E, 0x64, 0x3A, 0x00, 0xFF, 0xE3, 0xCB, 0xC7, 0x91, 0x9B, 0xC5, 0xFF,\n    0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x00, 0x0C, 0x18, 0x30, 0x30, 0x18, 0x0C, 0x00, 0xFF, 0xF3, 0xE7, 0xCF, 0xCF, 0xE7, 0xF3, 0xFF,\n    0x00, 0x30, 0x18, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0xFF, 0xCF, 0xE7, 0xF3, 0xF3, 0xE7, 0xCF, 0xFF,\n    0x00, 0x00, 0x24, 0x18, 0x7E, 0x18, 0x24, 0x00, 0xFF, 0xFF, 0xDB, 0xE7, 0x81, 0xE7, 0xDB, 0xFF,\n    0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0xFF, 0xFF, 0xE7, 0xE7, 0x81, 0xE7, 0xE7, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xCF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF,\n    0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, 0xFF, 0xF9, 0xF3, 0xE7, 0xCF, 0x9F, 0xBF, 0xFF,\n    0x00, 0x3C, 0x66, 0x6E, 0x76, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0x91, 0x89, 0x99, 0xC3, 0xFF,\n    0x00, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7E, 0x00, 0xFF, 0xE7, 0xC7, 0xE7, 0xE7, 0xE7, 0x81, 0xFF,\n    0x00, 0x3C, 0x66, 0x0C, 0x18, 0x30, 0x7E, 0x00, 0xFF, 0xC3, 0x99, 0xF3, 0xE7, 0xCF, 0x81, 0xFF,\n    0x00, 0x3C, 0x66, 0x0C, 0x06, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0xF3, 0xF9, 0x99, 0xC3, 0xFF,\n    0x00, 0x66, 0x66, 0x7E, 0x06, 0x06, 0x06, 0x00, 0xFF, 0x99, 0x99, 0x81, 0xF9, 0xF9, 0xF9, 0xFF,\n    0x00, 0x7E, 0x60, 0x7C, 0x06, 0x06, 0x7C, 0x00, 0xFF, 0x81, 0x9F, 0x83, 0xF9, 0xF9, 0x83, 0xFF,\n    0x00, 0x1C, 0x30, 0x7C, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0xE3, 0xCF, 0x83, 0x99, 0x99, 0xC3, 0xFF,\n    0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x00, 0xFF, 0x81, 0xF9, 0xF3, 0xE7, 0xCF, 0xCF, 0xFF,\n    0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0xC3, 0x99, 0x99, 0xC3, 0xFF,\n    0x00, 0x3C, 0x66, 0x3E, 0x06, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0xC1, 0xF9, 0x99, 0xC3, 0xFF,\n    0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xE7, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x30, 0x00, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xE7, 0xCF, 0xFF,\n    0x00, 0x00, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x00, 0xFF, 0xFF, 0xF3, 0xE7, 0xCF, 0xE7, 0xF3, 0xFF,\n    0x00, 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x81, 0xFF, 0xFF,\n    0x00, 0x00, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x00, 0xFF, 0xFF, 0xCF, 0xE7, 0xF3, 0xE7, 0xCF, 0xFF,\n    0x00, 0x3C, 0x66, 0x0C, 0x18, 0x00, 0x18, 0x00, 0xFF, 0xC3, 0x99, 0xF3, 0xE7, 0xFF, 0xE7, 0xFF,\n    0x00, 0x3C, 0x66, 0x6E, 0x6E, 0x60, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0x91, 0x91, 0x9F, 0xC3, 0xFF,\n    0x00, 0x18, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x00, 0xFF, 0xE7, 0xC3, 0x99, 0x81, 0x99, 0x99, 0xFF,\n    0x00, 0x7C, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00, 0xFF, 0x83, 0x99, 0x83, 0x99, 0x99, 0x83, 0xFF,\n    0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0x9F, 0x9F, 0x99, 0xC3, 0xFF,\n    0x00, 0x78, 0x6C, 0x66, 0x66, 0x6C, 0x78, 0x00, 0xFF, 0x87, 0x93, 0x99, 0x99, 0x93, 0x87, 0xFF,\n    0x00, 0x7E, 0x60, 0x78, 0x60, 0x60, 0x7E, 0x00, 0xFF, 0x81, 0x9F, 0x87, 0x9F, 0x9F, 0x81, 0xFF,\n    0x00, 0x7E, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, 0xFF, 0x81, 0x9F, 0x87, 0x9F, 0x9F, 0x9F, 0xFF,\n    0x00, 0x3C, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x9F, 0x91, 0x99, 0x99, 0xC3, 0xFF,\n    0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, 0xFF, 0x99, 0x99, 0x81, 0x99, 0x99, 0x99, 0xFF,\n    0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0xFF, 0xC3, 0xE7, 0xE7, 0xE7, 0xE7, 0xC3, 0xFF,\n    0x00, 0x1E, 0x06, 0x06, 0x06, 0x66, 0x3C, 0x00, 0xFF, 0xE1, 0xF9, 0xF9, 0xF9, 0x99, 0xC3, 0xFF,\n    0x00, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x00, 0xFF, 0x99, 0x93, 0x87, 0x87, 0x93, 0x99, 0xFF,\n    0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00, 0xFF, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x81, 0xFF,\n    0x00, 0x42, 0x66, 0x7E, 0x7E, 0x66, 0x66, 0x00, 0xFF, 0xBD, 0x99, 0x81, 0x81, 0x99, 0x99, 0xFF,\n    0x00, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x66, 0x00, 0xFF, 0x99, 0x89, 0x81, 0x91, 0x99, 0x99, 0xFF,\n    0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF,\n    0x00, 0x7C, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00, 0xFF, 0x83, 0x99, 0x83, 0x9F, 0x9F, 0x9F, 0xFF,\n    0x00, 0x3C, 0x66, 0x66, 0x6A, 0x6C, 0x3E, 0x00, 0xFF, 0xC3, 0x99, 0x99, 0x95, 0x93, 0xC1, 0xFF,\n    0x00, 0x7C, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00, 0xFF, 0x83, 0x99, 0x83, 0x87, 0x93, 0x99, 0xFF,\n    0x00, 0x3E, 0x60, 0x3C, 0x06, 0x06, 0x7C, 0x00, 0xFF, 0xC1, 0x9F, 0xC3, 0xF9, 0xF9, 0x83, 0xFF,\n    0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0xFF, 0x81, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xFF,\n    0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0xFF, 0x99, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xFF,\n    0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0xFF, 0x99, 0x99, 0x99, 0x99, 0xC3, 0xE7, 0xFF,\n    0x00, 0x66, 0x66, 0x7E, 0x7E, 0x66, 0x42, 0x00, 0xFF, 0x99, 0x99, 0x81, 0x81, 0x99, 0xBD, 0xFF,\n    0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, 0xFF, 0x99, 0xC3, 0xE7, 0xC3, 0x99, 0x99, 0xFF,\n    0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, 0xFF, 0x99, 0x99, 0xC3, 0xE7, 0xE7, 0xE7, 0xFF,\n    0x00, 0x7E, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00, 0xFF, 0x81, 0xF3, 0xE7, 0xCF, 0x9F, 0x81, 0xFF,\n    0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0xFF, 0xC3, 0xCF, 0xCF, 0xCF, 0xCF, 0xC3, 0xFF,\n    0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, 0xFF, 0x9F, 0xCF, 0xE7, 0xF3, 0xF9, 0xFD, 0xFF,\n    0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0xFF, 0xC3, 0xF3, 0xF3, 0xF3, 0xF3, 0xC3, 0xFF,\n    0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE7, 0xC3, 0x99, 0xFF, 0xFF, 0xFF, 0xFF,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF\n};\n\nconst char dev_bg[] = {\n    0x00, 0x00, 0x14, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\n    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,\n    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x00, 0x03, 0x00,\n    0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,\n    0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00,\n    0x03, 0x00, 0x03, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x05, 0x00,\n    0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00,\n    0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00,\n    0x05, 0x00, 0x01, 0x00, 0x04, 0x00, 0x06, 0x00, 0x07, 0x00, 0x0A, 0x00, 0x0B, 0x00, 0x08, 0x00,\n    0x09, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x26, 0x00, 0x27, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x02, 0x00, 0x04, 0x00, 0x16, 0x00,\n    0x17, 0x00, 0x1A, 0x00, 0x1B, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1E, 0x00, 0x1F, 0x00, 0x36, 0x00,\n    0x37, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x1C, 0x00,\n    0x1D, 0x00, 0x02, 0x00, 0x05, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00,\n    0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00,\n    0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x15, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,\n    0x01, 0x00, 0x01, 0x00\n};\n\n#endif\n\n#endif /* dev_menu_data_h */\n"
  },
  {
    "path": "sdl/main.c",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"config.h\"\n\n#include \"main.h\"\n#include \"core.h\"\n#include \"runner.h\"\n#include \"dev_menu.h\"\n#include \"settings.h\"\n#include \"system_paths.h\"\n#include \"utils.h\"\n#include \"boot_intro.h\"\n#include \"sdl_include.h\"\n\n#if SCREENSHOTS\n#include \"screenshot.h\"\n#endif\n\n#ifdef __EMSCRIPTEN__\n#include <emscripten.h>\n#endif\n\n#include <math.h>\n#include <string.h>\n\nconst char *defaultDisk = \"Disk.nx\";\nconst int defaultWindowScale = 4;\nconst int joyAxisThreshold = 16384;\n\nconst int keyboardControls[2][2][8] = {\n    // mapping 0\n    {\n        // up, down, left, right, button A, button B, alt. button A, alt. button B\n        {SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,\n            SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_N, SDL_SCANCODE_M},\n        {SDL_SCANCODE_E, SDL_SCANCODE_D, SDL_SCANCODE_S, SDL_SCANCODE_F,\n            SDL_SCANCODE_TAB, SDL_SCANCODE_Q, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_A}\n    },\n    // mapping 1\n    {\n        // up, down, left, right, button A, button B, alt. button A, alt. button B\n        {SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,\n            SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_I, SDL_SCANCODE_U},\n        {SDL_SCANCODE_UP, SDL_SCANCODE_DOWN, SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT,\n            SDL_SCANCODE_H, SDL_SCANCODE_L, SDL_SCANCODE_O, SDL_SCANCODE_Y}\n    }\n};\n\nvoid update(void *arg);\nvoid updateScreenRect(int winW, int winH);\nvoid configureJoysticks(void);\nvoid closeJoysticks(void);\nvoid setTouchPosition(int windowX, int windowY);\nvoid toggleZoom(void);\nvoid changeVolume(int delta);\nvoid audioCallback(void *userdata, Uint8 *stream, int len);\nvoid saveScreenshot(void *pixels, int scale);\n\n#ifdef __EMSCRIPTEN__\nvoid onloaded(const char *filename);\nvoid onerror(const char *filename);\n#endif\n\n\nSDL_Window *window = NULL;\nSDL_Renderer *renderer = NULL;\nSDL_Texture *texture = NULL;\nSDL_AudioDeviceID audioDevice = 0;\nSDL_AudioSpec audioSpec;\n\nstruct Runner runner;\n#if DEV_MENU\nstruct DevMenu devMenu;\n#endif\nstruct Settings settings;\nstruct CoreInput coreInput;\n\nenum MainState mainState = MainStateUndefined;\nchar mainProgramFilename[FILENAME_MAX] = \"\";\n\nint numJoysticks = 0;\nSDL_Joystick *joysticks[2] = {NULL, NULL};\nSDL_Rect screenRect;\nbool quit = false;\nbool releasedTouch = false;\nbool audioStarted = false;\nbool mouseEnabled = false;\nint messageNumber = 0;\nbool hasUsedInputLastUpdate = false;\nint screenshotRequestedWithScale = 0;\nint volume = 0; // 0 = max, it's a bit shift\n\nint main(int argc, const char * argv[])\n{\n    memset(&coreInput, 0, sizeof(struct CoreInput));\n    \n    settings_init(&settings, mainProgramFilename, argc, argv);\n    runner_init(&runner);\n#if DEV_MENU\n    dev_init(&devMenu, &runner, &settings);\n#endif\n    \n    if (runner_isOkay(&runner))\n    {\n        SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);\n        \n        SDL_EventState(SDL_DROPFILE, SDL_ENABLE);\n        SDL_Event event;\n        while (SDL_PollEvent(&event))\n        {\n            switch (event.type)\n            {\n                case SDL_DROPFILE: {\n                    strncpy(mainProgramFilename, event.drop.file, FILENAME_MAX - 1);\n                    SDL_free(event.drop.file);\n                    break;\n                }\n            }\n        }\n        \n        Uint32 windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;\n        if (settings.session.fullscreen)\n        {\n            windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;\n        }\n        \n        const char *windowTitle = \"LowRes NX\";\n        \n        window = SDL_CreateWindow(windowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH * defaultWindowScale, SCREEN_HEIGHT * defaultWindowScale, windowFlags);\n        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);\n        texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT);\n        \n        SDL_AudioSpec desiredAudioSpec = {\n            .freq = 44100,\n            .format = AUDIO_S16,\n            .channels = NUM_CHANNELS,\n#ifdef __EMSCRIPTEN__\n            .samples = 2048, // sample FRAMES\n#else\n            .samples = 1470, // sample FRAMES\n#endif\n            .userdata = runner.core,\n            .callback = audioCallback\n        };\n        \n        audioDevice = SDL_OpenAudioDevice(NULL, 0, &desiredAudioSpec, &audioSpec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);\n        \n        configureJoysticks();\n        \n        bootNX();\n        if (mainProgramFilename[0] != 0)\n        {\n            machine_poke(runner.core, bootIntroStateAddress, BootIntroStateProgramAvailable);\n        }\n        \n        int width, height;\n        SDL_GetWindowSize(window, &width, &height);\n        updateScreenRect(width, height);\n        \n#ifdef __EMSCRIPTEN__\n        emscripten_set_main_loop_arg(update, NULL, -1, true);\n#else\n        while (!quit)\n        {\n            Uint32 ticks = SDL_GetTicks();\n            \n            update(NULL);\n            \n            if (!settings.session.disabledelay || runner.core->machineInternals->isEnergySaving)\n            {\n                // limit to 60 FPS\n                Uint32 ticksDelta = SDL_GetTicks() - ticks;\n                if (ticksDelta < 16)\n                {\n                    SDL_Delay(16 - ticksDelta);\n                }\n            }\n        }\n        \n        core_willSuspendProgram(runner.core);\n#endif\n    }\n    \n    closeJoysticks();\n    \n    SDL_CloseAudioDevice(audioDevice);\n    \n    SDL_DestroyTexture(texture);\n    SDL_DestroyRenderer(renderer);\n    SDL_DestroyWindow(window);\n    \n    SDL_Quit();\n    \n    runner_deinit(&runner);\n    \n    return 0;\n}\n\nvoid bootNX()\n{\n    mainState = MainStateBootIntro;\n    \n    struct CoreError error = core_compileProgram(runner.core, bootIntroSourceCode, true);\n    if (error.code != ErrorNone)\n    {\n        core_traceError(runner.core, error);\n    }\n    \n    runner.core->interpreter->debug = false;\n    core_willRunProgram(runner.core, SDL_GetTicks() / 1000);\n}\n\nvoid rebootNX()\n{\n    core_willSuspendProgram(runner.core);\n    \n    mainProgramFilename[0] = 0;\n    bootNX();\n}\n\nbool hasProgram()\n{\n    return mainProgramFilename[0] != 0;\n}\n\nconst char *getMainProgramFilename()\n{\n    return mainProgramFilename;\n}\n\nvoid selectProgram(const char *filename)\n{\n    strncpy(mainProgramFilename, filename, FILENAME_MAX - 1);\n    if (mainState == MainStateBootIntro)\n    {\n        machine_poke(runner.core, bootIntroStateAddress, BootIntroStateProgramAvailable);\n    }\n    else\n    {\n        runMainProgram();\n    }\n}\n\nvoid runMainProgram()\n{\n    core_willSuspendProgram(runner.core);\n    \n    struct CoreError error = runner_loadProgram(&runner, mainProgramFilename);\n#if DEV_MENU\n    devMenu.lastError = error;\n#endif\n    if (error.code != ErrorNone)\n    {\n#if DEV_MENU\n        showDevMenu();\n#else\n        core_traceError(runner.core, error);\n#endif\n    }\n    else\n    {\n        core_willRunProgram(runner.core, SDL_GetTicks() / 1000);\n        mainState = MainStateRunningProgram;\n    }\n}\n\nvoid runToolProgram(const char *filename)\n{\n    core_willSuspendProgram(runner.core);\n    \n    struct CoreError error = runner_loadProgram(&runner, filename);\n    if (error.code == ErrorNone)\n    {\n        mainState = MainStateRunningTool;\n        runner.core->interpreter->debug = false;\n        core_willRunProgram(runner.core, SDL_GetTicks() / 1000);\n    }\n    else\n    {\n        core_traceError(runner.core, error);\n    }\n}\n\nvoid showDevMenu()\n{\n#if DEV_MENU\n    core_willSuspendProgram(runner.core);\n    \n    bool reload = (mainState == MainStateRunningTool);\n    mainState = MainStateDevMenu;\n    dev_show(&devMenu, reload);\n#endif\n}\n\nbool usesMainProgramAsDisk()\n{\n    return (mainState == MainStateRunningTool);\n}\n\nvoid getDiskFilename(char *outputString)\n{\n    if (usesMainProgramAsDisk())\n    {\n        strncpy(outputString, mainProgramFilename, FILENAME_MAX - 1);\n    }\n    else\n    {\n        strncpy(outputString, mainProgramFilename, FILENAME_MAX - 1);\n        char *separator = strrchr(outputString, PATH_SEPARATOR_CHAR);\n        if (separator)\n        {\n            separator++;\n            *separator = 0;\n            strncat(outputString, defaultDisk, FILENAME_MAX - 1);\n        }\n        else\n        {\n            strncpy(outputString, defaultDisk, FILENAME_MAX - 1);\n        }\n    }\n}\n\nvoid getRamFilename(char *outputString)\n{\n    char *prefPath = SDL_GetPrefPath(\"Inutilis Software\", \"LowRes NX\");\n    if (prefPath)\n    {\n        strncpy(outputString, prefPath, FILENAME_MAX - 1);\n        \n        char *separator = strrchr(mainProgramFilename, PATH_SEPARATOR_CHAR);\n        if (separator)\n        {\n            separator++;\n            strncat(outputString, separator, FILENAME_MAX - 1);\n        }\n        else\n        {\n            strncat(outputString, mainProgramFilename, FILENAME_MAX - 1);\n        }\n        \n        char *postfix = strrchr(outputString, '.');\n        if (postfix)\n        {\n            *postfix = 0;\n        }\n        strncat(outputString, \".dat\", FILENAME_MAX - 1);\n        \n    } else {\n        outputString[0] = 0;\n    }\n}\n\nvoid updateMouseMode()\n{\n    if (!mouseEnabled && (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP))\n    {\n        SDL_ShowCursor(SDL_DISABLE);\n    }\n    else\n    {\n        SDL_ShowCursor(SDL_ENABLE);\n    }\n}\n\nvoid setMouseEnabled(bool enabled)\n{\n    mouseEnabled = enabled;\n    updateMouseMode();\n}\n\nvoid update(void *arg)\n{\n    SDL_Event event;\n    bool hasInput = false;\n    bool forceRender = false;\n    \n    if (releasedTouch)\n    {\n        coreInput.touch = false;\n        releasedTouch = false;\n    }\n    \n    while (SDL_PollEvent(&event))\n    {\n        switch (event.type)\n        {\n            case SDL_QUIT:\n                quit = true;\n                break;\n                \n            case SDL_WINDOWEVENT:\n                switch (event.window.event)\n                {\n                    case SDL_WINDOWEVENT_RESIZED: {\n                        updateScreenRect(event.window.data1, event.window.data2);\n                        forceRender = true;\n                        break;\n                    }\n                }\n                break;\n            \n            case SDL_DROPFILE: {\n                if (hasPostfix(event.drop.file, \".nx\") || hasPostfix(event.drop.file, \".NX\"))\n                {\n#if DEV_MENU\n                    bool handled = (mainState == MainStateDevMenu && dev_handleDropFile(&devMenu, event.drop.file));\n                    if (!handled)\n                    {\n                        selectProgram(event.drop.file);\n                    }\n#else\n                    selectProgram(event.drop.file);\n#endif\n                    forceRender = true;\n                }\n                else\n                {\n                    overlay_message(runner.core, \"NOT NX FORMAT\");\n                }\n                SDL_free(event.drop.file);\n                break;\n            }\n            \n            case SDL_KEYDOWN: {\n                SDL_Keycode keycode = event.key.keysym.sym;\n                SDL_Scancode scancode = event.key.keysym.scancode;\n                \n                if (event.key.keysym.mod == 0)\n                {\n                    hasInput = true;\n                }\n                \n                // text input\n                if (keycode == SDLK_RETURN)\n                {\n                    coreInput.key = CoreInputKeyReturn;\n                }\n                else if (keycode == SDLK_BACKSPACE)\n                {\n                    coreInput.key = CoreInputKeyBackspace;\n                }\n                else if (scancode == SDL_SCANCODE_UP)\n                {\n                    coreInput.key = CoreInputKeyUp;\n                }\n                else if (scancode == SDL_SCANCODE_DOWN)\n                {\n                    coreInput.key = CoreInputKeyDown;\n                }\n                else if (scancode == SDL_SCANCODE_LEFT)\n                {\n                    coreInput.key = CoreInputKeyLeft;\n                }\n                else if (scancode == SDL_SCANCODE_RIGHT)\n                {\n                    coreInput.key = CoreInputKeyRight;\n                }\n                \n                // console buttons\n                if (keycode == SDLK_RETURN || keycode == SDLK_p)\n                {\n                    coreInput.pause = true;\n                }\n                \n#if HOT_KEYS\n                // system\n                if (event.key.keysym.mod & KMOD_CTRL)\n                {\n                    if (keycode == SDLK_d)\n                    {\n                        core_setDebug(runner.core, !core_getDebug(runner.core));\n                        if (core_getDebug(runner.core))\n                        {\n                            overlay_message(runner.core, \"DEBUG ON\");\n                        }\n                        else\n                        {\n                            overlay_message(runner.core, \"DEBUG OFF\");\n                        }\n                    }\n                    else if (keycode == SDLK_f)\n                    {\n                        if (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN_DESKTOP)\n                        {\n                            SDL_SetWindowFullscreen(window, 0);\n                        }\n                        else\n                        {\n                            SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);\n                        }\n                        updateMouseMode();\n                        forceRender = true;\n                    }\n                    else if (keycode == SDLK_r)\n                    {\n                        if (hasProgram())\n                        {\n                            runMainProgram();\n                            overlay_message(runner.core, \"RELOADED\");\n                        }\n                    }\n                    else if (keycode == SDLK_e)\n                    {\n                        rebootNX();\n                    }\n                    else if (keycode == SDLK_s)\n                    {\n                        screenshotRequestedWithScale = (event.key.keysym.mod & KMOD_SHIFT) ? 1 : 4;\n                        forceRender = true;\n                    }\n                    else if (keycode == SDLK_z)\n                    {\n                        toggleZoom();\n                        forceRender = true;\n                    }\n                    else if (keycode == SDLK_PLUS)\n                    {\n                        changeVolume(-1);\n                    }\n                    else if (keycode == SDLK_MINUS)\n                    {\n                        changeVolume(+1);\n                    }\n                }\n                else if (keycode == SDLK_ESCAPE)\n                {\n                    if (settings.session.disabledev)\n                    {\n                        quit = true;\n                    }\n#if DEV_MENU\n                    else if (hasProgram())\n                    {\n                        if (mainState != MainStateDevMenu)\n                        {\n                            showDevMenu();\n                        }\n                    }\n#endif\n                }\n                else if (settings.session.mapping == 1 && !core_isKeyboardEnabled(runner.core))\n                {\n                    if (keycode == SDLK_SPACE)\n                    {\n                        toggleZoom();\n                        forceRender = true;\n                        hasInput = false;\n                    }\n                    else if (scancode == SDL_SCANCODE_KP_PLUS)\n                    {\n                        changeVolume(-1);\n                    }\n                    else if (scancode == SDL_SCANCODE_KP_MINUS)\n                    {\n                        changeVolume(+1);\n                    }\n                }\n#endif\n                break;\n            }\n                \n            case SDL_TEXTINPUT: {\n                char key = event.text.text[0];\n                hasInput = true;\n                if (key >= ' ' && key <= '_')\n                {\n                    coreInput.key = key;\n                }\n                else if (key >= 'a' && key <= 'z')\n                {\n                    coreInput.key = key - 32;\n                }\n                break;\n            }\n            \n            case SDL_MOUSEBUTTONDOWN: {\n                hasInput = true;\n                setTouchPosition(event.button.x, event.button.y);\n                coreInput.touch = true;\n                break;\n            }\n                \n            case SDL_MOUSEBUTTONUP: {\n                releasedTouch = true;\n                break;\n            }\n                \n            case SDL_MOUSEMOTION: {\n                setTouchPosition(event.motion.x, event.motion.y);\n                break;\n            }\n                \n            case SDL_JOYDEVICEADDED:\n            case SDL_JOYDEVICEREMOVED: {\n                configureJoysticks();\n                break;\n            }\n                \n            case SDL_JOYBUTTONDOWN: {\n                hasInput = true;\n                if (event.jbutton.button == 2)\n                {\n                    coreInput.pause = true;\n                }\n                break;\n            }\n        }\n    }\n    \n    const Uint8 *state = SDL_GetKeyboardState(NULL);\n    for (int i = 0; i < 2; i++)\n    {\n        struct CoreInputGamepad *gamepad = &coreInput.gamepads[i];\n        if (i < numJoysticks)\n        {\n            SDL_Joystick *joy = joysticks[i];\n            Uint8 hat = SDL_JoystickGetHat(joy, 0);\n            Sint16 axisX = SDL_JoystickGetAxis(joy, 0);\n            Sint16 axisY = SDL_JoystickGetAxis(joy, 1);\n            gamepad->up = (hat & SDL_HAT_UP) != 0 || axisY < -joyAxisThreshold;\n            gamepad->down = (hat & SDL_HAT_DOWN) != 0 || axisY > joyAxisThreshold;\n            gamepad->left = (hat & SDL_HAT_LEFT) != 0 || axisX < -joyAxisThreshold;\n            gamepad->right = (hat & SDL_HAT_RIGHT) != 0 || axisX > joyAxisThreshold;\n            gamepad->buttonA = SDL_JoystickGetButton(joy, 0);\n            gamepad->buttonB = SDL_JoystickGetButton(joy, 1);\n        }\n        else\n        {\n            int ci = i - numJoysticks;\n            int m = settings.session.mapping;\n            gamepad->up = state[keyboardControls[m][ci][0]];\n            gamepad->down = state[keyboardControls[m][ci][1]];\n            gamepad->left = state[keyboardControls[m][ci][2]];\n            gamepad->right = state[keyboardControls[m][ci][3]];\n            gamepad->buttonA = state[keyboardControls[m][ci][4]] || state[keyboardControls[m][ci][6]];\n            gamepad->buttonB = state[keyboardControls[m][ci][5]] || state[keyboardControls[m][ci][7]];\n        }\n    }\n    \n    switch (mainState)\n    {\n        case MainStateUndefined:\n            break;\n            \n        case MainStateBootIntro:\n            if (hasInput && !hasProgram())\n            {\n                // user hint\n                overlay_message(runner.core, \"DRAG .NX INTO WINDOW\");\n            }\n            core_update(runner.core, &coreInput);\n            if (machine_peek(runner.core, bootIntroStateAddress) == BootIntroStateReadyToRun)\n            {\n                machine_poke(runner.core, bootIntroStateAddress, BootIntroStateDone);\n#ifdef __EMSCRIPTEN__\n                emscripten_async_wget(mainProgramFilename, mainProgramFilename, onloaded, onerror);\n#else\n                runMainProgram();\n#endif\n            }\n            break;\n            \n        case MainStateRunningProgram:\n        case MainStateRunningTool:\n            core_update(runner.core, &coreInput);\n            if (hasInput)\n            {\n                if (runner.core->interpreter->state == StateEnd)\n                {\n                    overlay_message(runner.core, \"END OF PROGRAM\");\n                }\n                else if (!coreInput.out_hasUsedInput && !hasUsedInputLastUpdate)\n                {\n                    // user hints for controls\n                    union IOAttributes attr = runner.core->machine->ioRegisters.attr;\n                    if (attr.touchEnabled && !attr.keyboardEnabled)\n                    {\n                        overlay_message(runner.core, \"TOUCH/MOUSE\");\n                    }\n                    if (attr.keyboardEnabled && !attr.touchEnabled)\n                    {\n                        overlay_message(runner.core, \"KEYBOARD\");\n                    }\n                    if (attr.gamepadsEnabled && !attr.keyboardEnabled && settings.session.mapping == 0)\n                    {\n                        if (attr.gamepadsEnabled == 2)\n                        {\n                            if (messageNumber % 2 == 1)\n                            {\n                                overlay_message(runner.core, \"P2 ]:ESDF [:TAB \\\\:Q\");\n                            }\n                            else\n                            {\n                                overlay_message(runner.core, \"P1 ]:ARROWS [:N \\\\:M\");\n                            }\n                            messageNumber++;\n                        }\n                        else\n                        {\n                            overlay_message(runner.core, \"]:ARROWS [:Z \\\\:X\");\n                        }\n                    }\n                }\n            }\n            break;\n            \n        case MainStateDevMenu:\n#if DEV_MENU\n            dev_update(&devMenu, &coreInput);\n#endif\n            break;\n    }\n    \n    hasUsedInputLastUpdate = coreInput.out_hasUsedInput;\n    \n    if (!audioStarted && audioDevice)\n    {\n        audioStarted = true;\n        SDL_PauseAudioDevice(audioDevice, 0);\n    }\n    \n    if (core_shouldRender(runner.core) || forceRender)\n    {\n        SDL_RenderClear(renderer);\n        \n        void *pixels = NULL;\n        int pitch = 0;\n        SDL_LockTexture(texture, NULL, &pixels, &pitch);\n        \n        video_renderScreen(runner.core, pixels);\n        \n        if (screenshotRequestedWithScale > 0)\n        {\n            saveScreenshot(pixels, screenshotRequestedWithScale);\n            screenshotRequestedWithScale = 0;\n        }\n        \n        SDL_UnlockTexture(texture);\n        SDL_RenderCopy(renderer, texture, NULL, &screenRect);\n        \n        SDL_RenderPresent(renderer);\n    }\n}\n\nvoid updateScreenRect(int winW, int winH)\n{\n    switch (settings.session.zoom)\n    {\n        case ZoomPixelPerfect: {\n            int factor = fmax(1, fmin(winW / SCREEN_WIDTH, winH / SCREEN_HEIGHT));\n            \n            int nxScreenW = SCREEN_WIDTH * factor;\n            int nxScreenH = SCREEN_HEIGHT * factor;\n            \n            screenRect.w = nxScreenW;\n            screenRect.h = nxScreenH;\n            screenRect.x = (winW - nxScreenW) / 2;\n            screenRect.y = (winH - nxScreenH) / 2;\n            break;\n        }\n        case ZoomLarge: {\n            float factor = fmax(1, fmin(winW / (float)SCREEN_WIDTH, winH / (float)SCREEN_HEIGHT));\n            \n            int nxScreenW = SCREEN_WIDTH * factor;\n            int nxScreenH = SCREEN_HEIGHT * factor;\n            \n            screenRect.w = nxScreenW;\n            screenRect.h = nxScreenH;\n            screenRect.x = (winW - nxScreenW) / 2;\n            screenRect.y = (winH - nxScreenH) / 2;\n            break;\n        }\n        case ZoomOverscan: {\n            float factor = fmax(winW / (float)SCREEN_WIDTH, winH / (float)SCREEN_HEIGHT);\n            \n            int nxScreenW = SCREEN_WIDTH * factor;\n            int nxScreenH = SCREEN_HEIGHT * factor;\n            \n            screenRect.w = nxScreenW;\n            screenRect.h = nxScreenH;\n            screenRect.x = (winW - nxScreenW) / 2;\n            screenRect.y = (winH - nxScreenH) / 2;\n            break;\n        }\n        case ZoomSqueeze:\n            screenRect.w = winW;\n            screenRect.h = winH;\n            screenRect.x = 0;\n            screenRect.y = 0;\n            break;\n    }\n\n    SDL_SetTextInputRect(&screenRect);\n}\n\nvoid configureJoysticks() {\n    closeJoysticks();\n    numJoysticks = SDL_NumJoysticks();\n    if (numJoysticks > 2)\n    {\n        numJoysticks = 2;\n    }\n    for (int i = 0; i < numJoysticks; i++)\n    {\n        joysticks[i] = SDL_JoystickOpen(i);\n    }\n}\n\nvoid closeJoysticks() {\n    for (int i = 0; i < numJoysticks; i++)\n    {\n        SDL_JoystickClose(joysticks[i]);\n        joysticks[i] = NULL;\n    }\n    numJoysticks = 0;\n}\n\nvoid setTouchPosition(int windowX, int windowY)\n{\n    coreInput.touchX = (windowX - screenRect.x) * SCREEN_WIDTH / screenRect.w;\n    coreInput.touchY = (windowY - screenRect.y) * SCREEN_HEIGHT / screenRect.h;\n}\n\nvoid toggleZoom()\n{\n    settings.session.zoom = (settings.session.zoom + 1) % 4;\n    int width, height;\n    SDL_GetWindowSize(window, &width, &height);\n    updateScreenRect(width, height);\n}\n\nvoid changeVolume(int delta)\n{\n    volume += delta;\n    if (volume < 0) volume = 0;\n    if (volume > 6) volume = 6;\n    char message[16];\n    sprintf(message, \"VOLUME %d%%\", 100 >> volume);\n    overlay_message(runner.core, message);\n}\n\nvoid audioCallback(void *userdata, Uint8 *stream, int len)\n{\n    int16_t *samples = (int16_t *)stream;\n    int numSamples = len / NUM_CHANNELS;\n    audio_renderAudio(userdata, samples, numSamples, audioSpec.freq, volume);\n}\n\nvoid saveScreenshot(void *pixels, int scale)\n{\n#if SCREENSHOTS\n    bool succeeded = screenshot_save(pixels, scale);\n    if (succeeded)\n    {\n        overlay_message(runner.core, \"SCREENSHOT SAVED\");\n    }\n    else\n    {\n        overlay_message(runner.core, \"SCREENSHOT ERROR\");\n    }\n#endif\n}\n\n#ifdef __EMSCRIPTEN__\n\nvoid onloaded(const char *filename)\n{\n    runMainProgram();\n}\n\nvoid onerror(const char *filename)\n{\n}\n\n#endif\n"
  },
  {
    "path": "sdl/main.h",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef main_h\n#define main_h\n\n#include <stdbool.h>\n\nenum MainState {\n    MainStateUndefined,\n    MainStateBootIntro,\n    MainStateRunningProgram,\n    MainStateRunningTool,\n    MainStateDevMenu,\n};\n\nenum Zoom {\n    ZoomPixelPerfect,\n    ZoomLarge,\n    ZoomOverscan,\n    ZoomSqueeze,\n};\n\nvoid bootNX(void);\nvoid rebootNX(void);\nbool hasProgram(void);\nconst char *getMainProgramFilename(void);\nvoid selectProgram(const char *filename);\nvoid runMainProgram(void);\nvoid runToolProgram(const char *filename);\nvoid showDevMenu(void);\nbool usesMainProgramAsDisk(void);\nvoid getDiskFilename(char *outputString);\nvoid getRamFilename(char *outputString);\nvoid setMouseEnabled(bool enabled);\n\n#endif /* main_h */\n"
  },
  {
    "path": "sdl/runner.c",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"runner.h\"\n#include \"main.h\"\n#include \"sdl_include.h\"\n#include \"system_paths.h\"\n#include <string.h>\n#include <stdlib.h>\n\nvoid interpreterDidFail(void *context, struct CoreError coreError);\nbool diskDriveWillAccess(void *context, struct DataManager *diskDataManager);\nvoid diskDriveDidSave(void *context, struct DataManager *diskDataManager);\nvoid diskDriveIsFull(void *context, struct DataManager *diskDataManager);\nvoid controlsDidChange(void *context, struct ControlsInfo controlsInfo);\nvoid persistentRamWillAccess(void *context, uint8_t *destination, int size);\nvoid persistentRamDidChange(void *context, uint8_t *data, int size);\n\n\nvoid runner_init(struct Runner *runner)\n{\n    memset(runner, 0, sizeof(struct Runner));\n    \n    struct Core *core = calloc(1, sizeof(struct Core));\n    if (core)\n    {\n        core_init(core);\n        \n        runner->coreDelegate.context = runner;\n        runner->coreDelegate.interpreterDidFail = interpreterDidFail;\n        runner->coreDelegate.diskDriveWillAccess = diskDriveWillAccess;\n        runner->coreDelegate.diskDriveDidSave = diskDriveDidSave;\n        runner->coreDelegate.diskDriveIsFull = diskDriveIsFull;\n        runner->coreDelegate.controlsDidChange = controlsDidChange;\n        runner->coreDelegate.persistentRamWillAccess = persistentRamWillAccess;\n        runner->coreDelegate.persistentRamDidChange = persistentRamDidChange;\n        \n        core_setDelegate(core, &runner->coreDelegate);\n\n        runner->core = core;\n    }\n}\n\nvoid runner_deinit(struct Runner *runner)\n{\n    if (runner->core)\n    {\n        core_deinit(runner->core);\n        \n        free(runner->core);\n        runner->core = NULL;\n    }\n}\n\nbool runner_isOkay(struct Runner *runner)\n{\n    return (runner->core != NULL);\n}\n\nstruct CoreError runner_loadProgram(struct Runner *runner, const char *filename)\n{\n    struct CoreError error = err_noCoreError();\n    \n    FILE *file = fopen_utf8(filename, \"rb\");\n    if (file)\n    {\n        fseek(file, 0, SEEK_END);\n        long size = ftell(file);\n        fseek(file, 0, SEEK_SET);\n        \n        char *sourceCode = calloc(1, size + 1); // +1 for terminator\n        if (sourceCode)\n        {\n            fread(sourceCode, size, 1, file);\n            \n            error = core_compileProgram(runner->core, sourceCode, true);\n            free(sourceCode);\n        }\n        else\n        {\n            error = err_makeCoreError(ErrorOutOfMemory, -1);\n        }\n        \n        fclose(file);\n    }\n    else\n    {\n        error = err_makeCoreError(ErrorCouldNotOpenProgram, -1);\n    }\n    \n    return error;\n}\n\n/** Called on error */\nvoid interpreterDidFail(void *context, struct CoreError coreError)\n{\n    struct Runner *runner = context;\n    core_traceError(runner->core, coreError);\n}\n\n/** Returns true if the disk is ready, false if not. In case of not, core_diskLoaded must be called when ready. */\nbool diskDriveWillAccess(void *context, struct DataManager *diskDataManager)\n{\n    struct Runner *runner = context;\n    if (!runner->messageShownUsingDisk && !usesMainProgramAsDisk())\n    {\n#ifdef __EMSCRIPTEN__\n        overlay_message(runner->core, \"NO DISK\");\n#else\n        overlay_message(runner->core, \"USING DISK.NX\");\n#endif\n        runner->messageShownUsingDisk = true;\n    }\n    \n#ifndef __EMSCRIPTEN__\n    \n    char diskFilename[FILENAME_MAX];\n    getDiskFilename(diskFilename);\n    \n    FILE *file = fopen_utf8(diskFilename, \"rb\");\n    if (file)\n    {\n        fseek(file, 0, SEEK_END);\n        long size = ftell(file);\n        fseek(file, 0, SEEK_SET);\n        \n        char *sourceCode = calloc(1, size + 1); // +1 for terminator\n        if (sourceCode)\n        {\n            fread(sourceCode, size, 1, file);\n            \n            struct CoreError error = data_import(diskDataManager, sourceCode, true);\n            free(sourceCode);\n            \n            if (error.code != ErrorNone)\n            {\n                core_traceError(runner->core, error);\n            }\n        }\n        else\n        {\n            struct TextLib *lib = &runner->core->overlay->textLib;\n            txtlib_printText(lib, \"NOT ENOUGH MEMORY\\n\");\n        }\n        \n        fclose(file);\n    }\n    \n#endif\n    \n    return true;\n}\n\n/** Called when a disk data entry was saved */\nvoid diskDriveDidSave(void *context, struct DataManager *diskDataManager)\n{\n    struct Runner *runner = context;\n#ifdef __EMSCRIPTEN__\n    overlay_message(runner->core, \"NO DISK\");\n#else\n    char *output = data_export(diskDataManager);\n    if (output)\n    {\n        char diskFilename[FILENAME_MAX];\n        getDiskFilename(diskFilename);\n        \n        FILE *file = fopen_utf8(diskFilename, \"wb\");\n        if (file)\n        {\n            fwrite(output, 1, strlen(output), file);\n            fclose(file);\n        }\n        else\n        {\n            struct TextLib *lib = &runner->core->overlay->textLib;\n            txtlib_printText(lib, \"COULD NOT SAVE:\\n\");\n            txtlib_printText(lib, diskFilename);\n            txtlib_printText(lib, \"\\n\");\n        }\n        \n        free(output);\n    }\n#endif\n}\n\n/** Called when a disk data entry was tried to be saved, but the disk is full */\nvoid diskDriveIsFull(void *context, struct DataManager *diskDataManager)\n{\n    struct Runner *runner = context;\n    overlay_message(runner->core, \"DISK IS FULL\");\n}\n\n/** Called when keyboard or gamepad settings changed */\nvoid controlsDidChange(void *context, struct ControlsInfo controlsInfo)\n{\n    if (   controlsInfo.keyboardMode == KeyboardModeOn\n        || (controlsInfo.keyboardMode == KeyboardModeOptional && !SDL_HasScreenKeyboardSupport()) )\n    {\n        if (!SDL_IsTextInputActive())\n        {\n            SDL_StartTextInput();\n        }\n    }\n    else if (SDL_IsTextInputActive())\n    {\n        SDL_StopTextInput();\n    }\n    setMouseEnabled(controlsInfo.isTouchEnabled);\n}\n\n/** Called when persistent RAM will be accessed the first time */\nvoid persistentRamWillAccess(void *context, uint8_t *destination, int size)\n{\n#ifndef __EMSCRIPTEN__\n    char ramFilename[FILENAME_MAX];\n    getRamFilename(ramFilename);\n    \n    FILE *file = fopen_utf8(ramFilename, \"rb\");\n    if (file)\n    {\n        fread(destination, sizeof(uint8_t), size, file);\n        fclose(file);\n    }\n#endif\n}\n\n/** Called when persistent RAM should be saved */\nvoid persistentRamDidChange(void *context, uint8_t *data, int size)\n{\n#ifndef __EMSCRIPTEN__\n    struct Runner *runner = context;\n    \n    char ramFilename[FILENAME_MAX];\n    getRamFilename(ramFilename);\n    \n    FILE *file = fopen_utf8(ramFilename, \"wb\");\n    if (file)\n    {\n        fwrite(data, 1, size, file);\n        fclose(file);\n    }\n    else\n    {\n        struct TextLib *lib = &runner->core->overlay->textLib;\n        txtlib_printText(lib, \"COULD NOT SAVE:\\n\");\n        txtlib_printText(lib, ramFilename);\n        txtlib_printText(lib, \"\\n\");\n    }\n#endif\n}\n"
  },
  {
    "path": "sdl/runner.h",
    "content": "//\n// Copyright 2017-2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef runner_h\n#define runner_h\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"core.h\"\n\nstruct Runner {\n    struct Core *core;\n    struct CoreDelegate coreDelegate;\n    bool messageShownUsingDisk;\n};\n\nvoid runner_init(struct Runner *runner);\nvoid runner_deinit(struct Runner *runner);\nbool runner_isOkay(struct Runner *runner);\nstruct CoreError runner_loadProgram(struct Runner *runner, const char *filename);\n\n#endif /* runner_h */\n"
  },
  {
    "path": "sdl/screenshot.c",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"config.h\"\n\n#if SCREENSHOTS\n\n#include \"screenshot.h\"\n#include \"system_paths.h\"\n#include \"core.h\"\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#define STB_IMAGE_WRITE_IMPLEMENTATION\n#include \"stb_image_write.h\"\n\nbool writeImage(const char *filename, int width, int height, uint32_t *pixels, int scale)\n{\n    uint8_t *data = malloc(width * height * 3 * scale * scale);\n    if (data)\n    {\n        int i = 0;\n        for (int y = 0; y < height; y++)\n        {\n            for (int ys = 0; ys < scale; ys++)\n            {\n                for (int x = 0; x < width; x++)\n                {\n                    uint32_t pixel = pixels[y * width + x];\n                    for (int xs = 0; xs < scale; xs++)\n                    {\n                        data[i++] = (pixel) & 0xFF;\n                        data[i++] = (pixel >> 8) & 0xFF;\n                        data[i++] = (pixel >> 16) & 0xFF;\n                    }\n                }\n            }\n        }\n        \n        int result = stbi_write_png(filename, width * scale, height * scale, 3, data, width * 3 * scale);\n        free(data);\n        \n        return (result != 0);\n    }\n    return false;\n}\n\nbool screenshot_save(uint32_t *pixels, int scale)\n{\n    char filename[FILENAME_MAX];\n    \n    desktop_path(filename, FILENAME_MAX);\n    size_t len = strlen(filename);\n    \n    time_t rawtime;\n    time(&rawtime);\n    struct tm *timeinfo = localtime(&rawtime);\n    strftime(&filename[len], FILENAME_MAX - len - 1, \"LowRes NX %Y-%m-%d %H_%M_%S.png\", timeinfo);\n\n    return writeImage(filename, SCREEN_WIDTH, SCREEN_HEIGHT, pixels, scale);\n}\n\n#endif\n"
  },
  {
    "path": "sdl/screenshot.h",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef screenshot_h\n#define screenshot_h\n\n#include \"config.h\"\n\n#if SCREENSHOTS\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nbool screenshot_save(uint32_t *pixels, int scale);\n\n#endif\n\n#endif /* screenshot_h */\n"
  },
  {
    "path": "sdl/sdl_include.h",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#if defined(_WIN32)\n#include <SDL.h>\n#else\n#include <SDL2/SDL.h>\n#endif\n"
  },
  {
    "path": "sdl/settings.c",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"config.h\"\n\n#include \"settings.h\"\n#include \"system_paths.h\"\n#include \"utils.h\"\n#include \"sdl_include.h\"\n#include <string.h>\n\nconst char *optionYes = \"yes\";\nconst char *optionNo = \"no\";\n\nbool settings_filename(char *destination);\nvoid settings_setParameter(struct Parameters *parameters, const char *key, const char *value);\nvoid settings_saveAs(struct Settings *settings, const char *filename);\n\nvoid settings_init(struct Settings *settings, char *filenameOut, int argc, const char * argv[])\n{\n    memset(settings, 0, sizeof(struct Settings));\n    \n#if SETTINGS_FILE\n    \n    // load settings file\n    \n    char filename[FILENAME_MAX];\n    if (settings_filename(filename))\n    {\n        FILE *file = fopen_utf8(filename, \"r\");\n        if (file)\n        {\n            char line[FILENAME_MAX];\n            while (fgets(line, FILENAME_MAX, file))\n            {\n                if (line[0] != '#')\n                {\n                    char *space = strchr(line, ' ');\n                    if (space)\n                    {\n                        *space = 0; // separate into two strings\n                        char *value = space + 1;\n                        \n                        // remove EOL characters\n                        char *eolChar = strchr(value, '\\n');\n                        if (eolChar)\n                        {\n                            *eolChar = 0;\n                        }\n                        eolChar = strchr(value, '\\r');\n                        if (eolChar)\n                        {\n                            *eolChar = 0;\n                        }\n                        \n                        if (strcmp(line, \"tool\") == 0)\n                        {\n                            settings_addTool(settings, value);\n                        }\n                        else\n                        {\n                            settings_setParameter(&settings->file, line, value);\n                        }\n                    }\n                }\n            }\n            \n            fclose(file);\n        }\n        else\n        {\n            // write default settings file\n            settings_saveAs(settings, filename);\n        }\n        \n        // copy file parameters to session parameters\n        memcpy(&settings->session, &settings->file, sizeof(struct Parameters));\n    }\n    \n#endif\n    \n    // parse arguments\n    \n    for (int i = 1; i < argc; i++)\n    {\n        const char *arg = argv[i];\n        if (*arg == '-') {\n            i++;\n            if (i < argc)\n            {\n                settings_setParameter(&settings->session, arg + 1, argv[i]);\n            }\n            else\n            {\n                printf(\"missing value for parameter %s\\n\", arg);\n            }\n        } else {\n            strncpy(filenameOut, arg, FILENAME_MAX - 1);\n        }\n    }\n}\n\nbool settings_filename(char *destination)\n{\n#if SETTINGS_FILE\n    char *prefPath = SDL_GetPrefPath(\"Inutilis Software\", \"LowRes NX\");\n    if (prefPath)\n    {\n        strncpy(destination, prefPath, FILENAME_MAX - 1);\n        strncat(destination, \"settings.txt\", FILENAME_MAX - 1);\n        SDL_free((void *)prefPath);\n        return true;\n    }\n#endif\n    return false;\n}\n\nvoid settings_setParameter(struct Parameters *parameters, const char *key, const char *value)\n{\n    if (strcmp(key, \"fullscreen\") == 0) {\n        if (strcmp(value, optionYes) == 0)\n        {\n            parameters->fullscreen = true;\n        }\n        else if (strcmp(value, optionNo) == 0)\n        {\n            parameters->fullscreen = false;\n        }\n    }\n    else if (strcmp(key, \"disabledev\") == 0)\n    {\n        if (strcmp(value, optionYes) == 0)\n        {\n            parameters->disabledev = true;\n        }\n        else if (strcmp(value, optionNo) == 0)\n        {\n            parameters->disabledev = false;\n        }\n    }\n    else if (strcmp(key, \"mapping\") == 0)\n    {\n        int i = atoi(value);\n        if (i >= 0 && i <= 1)\n        {\n            parameters->mapping = i;\n        }\n    }\n    else if (strcmp(key, \"disabledelay\") == 0)\n    {\n        if (strcmp(value, optionYes) == 0)\n        {\n            parameters->disabledelay = true;\n        }\n        else if (strcmp(value, optionNo) == 0)\n        {\n            parameters->disabledelay = false;\n        }\n    }\n    else if (strcmp(key, \"zoom\") == 0)\n    {\n        int i = atoi(value);\n        if (i >= 0 && i <= 3)\n        {\n            parameters->zoom = i;\n        }\n    }\n    else\n    {\n        printf(\"unknown parameter %s\\n\", key);\n    }\n}\n\nvoid settings_save(struct Settings *settings)\n{\n#if SETTINGS_FILE\n    char filename[FILENAME_MAX];\n    if (settings_filename(filename))\n    {\n        settings_saveAs(settings, filename);\n    }\n#endif\n}\n\nvoid settings_saveAs(struct Settings *settings, const char *filename)\n{\n#if SETTINGS_FILE\n    FILE *file = fopen_utf8(filename, \"w\");\n    if (file)\n    {\n        fputs(\"# Start the application in fullscreen mode.\\n# fullscreen yes/no\\n\", file);\n        fputs(\"fullscreen \", file);\n        fputs(settings->file.fullscreen ? optionYes : optionNo, file);\n        fputs(\"\\n\\n\", file);\n        \n        fputs(\"# Start the application in zoom mode: 0 = pixel perfect, 1 = large, 2 = overscan, 3 = squeeze.\\n# zoom 0-3\\n\", file);\n        fprintf(file, \"zoom %d\\n\\n\", settings->file.zoom);\n        \n        fputs(\"# Disable the Development Menu, Esc key quits LowRes NX.\\n# disabledev yes/no\\n\", file);\n        fputs(\"disabledev \", file);\n        fputs(settings->file.disabledev ? optionYes : optionNo, file);\n        fputs(\"\\n\\n\", file);\n        \n        fputs(\"# Set the key mapping. 0 = standard, 1 = GameShell.\\n# mapping 0-1\\n\", file);\n        fprintf(file, \"mapping %d\\n\\n\", settings->file.mapping);\n        \n        fputs(\"# Disable the delay for too short frames.\\n# disabledelay yes/no\\n\", file);\n        fputs(\"disabledelay \", file);\n        fputs(settings->file.disabledelay ? optionYes : optionNo, file);\n        fputs(\"\\n\\n\", file);\n        \n        fputs(\"# Add tools for the Edit ROM menu (max 4).\\n# tool My Tool.nx\\n\", file);\n        for (int i = 0; i < settings->numTools; i++)\n        {\n            fputs(\"tool \", file);\n            fputs(settings->tools[i], file);\n            fputs(\"\\n\", file);\n        }\n        \n        fclose(file);\n    }\n#endif\n}\n\nbool settings_addTool(struct Settings *settings, const char *filename)\n{\n    int index = settings->numTools;\n    if (index < MAX_TOOLS)\n    {\n        strncpy(settings->tools[index], filename, FILENAME_MAX - 1);\n        displayName(filename, settings->toolNames[index], TOOL_NAME_SIZE);\n        settings->numTools++;\n        return true;\n    }\n    return false;\n}\n\nvoid settings_removeTool(struct Settings *settings, int index)\n{\n    if (index < settings->numTools)\n    {\n        for (int i = index; i < MAX_TOOLS - 1; i++)\n        {\n            strncpy(settings->tools[i], settings->tools[i + 1], FILENAME_MAX - 1);\n            strncpy(settings->toolNames[i], settings->toolNames[i + 1], TOOL_NAME_SIZE - 1);\n        }\n        settings->tools[MAX_TOOLS - 1][0] = 0;\n        settings->toolNames[MAX_TOOLS - 1][0] = 0;\n        settings->numTools--;\n    }\n}\n"
  },
  {
    "path": "sdl/settings.h",
    "content": "//\n// Copyright 2017-2020 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef settings_h\n#define settings_h\n\n#include <stdio.h>\n#include <stdbool.h>\n\n#define MAX_TOOLS 4\n#define TOOL_NAME_SIZE 21\n\nstruct Parameters {\n    bool fullscreen;\n    int zoom;\n    bool disabledev;\n    int mapping;\n    int disabledelay;\n};\n\nstruct Settings {\n    struct Parameters file;\n    struct Parameters session;\n    int numTools;\n    char tools[MAX_TOOLS][FILENAME_MAX];\n    char toolNames[MAX_TOOLS][TOOL_NAME_SIZE];\n};\n\nvoid settings_init(struct Settings *settings, char *filenameOut, int argc, const char * argv[]);\nvoid settings_save(struct Settings *settings);\nbool settings_addTool(struct Settings *settings, const char *filename);\nvoid settings_removeTool(struct Settings *settings, int index);\n\n#endif /* settings_h */\n"
  },
  {
    "path": "sdl/stb_image_write.h",
    "content": "/* stb_image_write - v1.09 - public domain - http://nothings.org/stb/stb_image_write.h\n   writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015\n                                     no warranty implied; use at your own risk\n\n   Before #including,\n\n       #define STB_IMAGE_WRITE_IMPLEMENTATION\n\n   in the file that you want to have the implementation.\n\n   Will probably not work correctly with strict-aliasing optimizations.\n\n   If using a modern Microsoft Compiler, non-safe versions of CRT calls may cause \n   compilation warnings or even errors. To avoid this, also before #including,\n\n       #define STBI_MSC_SECURE_CRT\n\nABOUT:\n\n   This header file is a library for writing images to C stdio. It could be\n   adapted to write to memory or a general streaming interface; let me know.\n\n   The PNG output is not optimal; it is 20-50% larger than the file\n   written by a decent optimizing implementation; though providing a custom\n   zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that.\n   This library is designed for source code compactness and simplicity,\n   not optimal image file size or run-time performance.\n\nBUILDING:\n\n   You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h.\n   You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace\n   malloc,realloc,free.\n   You can #define STBIW_MEMMOVE() to replace memmove()\n   You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function\n   for PNG compression (instead of the builtin one), it must have the following signature:\n   unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality);\n   The returned data will be freed with STBIW_FREE() (free() by default),\n   so it must be heap allocated with STBIW_MALLOC() (malloc() by default),\n\nUSAGE:\n\n   There are five functions, one for each image file format:\n\n     int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes);\n     int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data);\n     int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data);\n     int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality);\n     int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);\n\n     void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically\n\n   There are also five equivalent functions that use an arbitrary write function. You are\n   expected to open/close your file-equivalent before and after calling these:\n\n     int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data, int stride_in_bytes);\n     int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\n     int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\n     int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);\n     int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality);\n\n   where the callback is:\n      void stbi_write_func(void *context, void *data, int size);\n\n   You can configure it with these global variables:\n      int stbi_write_tga_with_rle;             // defaults to true; set to 0 to disable RLE\n      int stbi_write_png_compression_level;    // defaults to 8; set to higher for more compression\n      int stbi_write_force_png_filter;         // defaults to -1; set to 0..5 to force a filter mode\n\n\n   You can define STBI_WRITE_NO_STDIO to disable the file variant of these\n   functions, so the library will not use stdio.h at all. However, this will\n   also disable HDR writing, because it requires stdio for formatted output.\n\n   Each function returns 0 on failure and non-0 on success.\n\n   The functions create an image file defined by the parameters. The image\n   is a rectangle of pixels stored from left-to-right, top-to-bottom.\n   Each pixel contains 'comp' channels of data stored interleaved with 8-bits\n   per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is\n   monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall.\n   The *data pointer points to the first byte of the top-left-most pixel.\n   For PNG, \"stride_in_bytes\" is the distance in bytes from the first byte of\n   a row of pixels to the first byte of the next row of pixels.\n\n   PNG creates output files with the same number of components as the input.\n   The BMP format expands Y to RGB in the file format and does not\n   output alpha.\n\n   PNG supports writing rectangles of data even when the bytes storing rows of\n   data are not consecutive in memory (e.g. sub-rectangles of a larger image),\n   by supplying the stride between the beginning of adjacent rows. The other\n   formats do not. (Thus you cannot write a native-format BMP through the BMP\n   writer, both because it is in BGR order and because it may have padding\n   at the end of the line.)\n\n   PNG allows you to set the deflate compression level by setting the global\n   variable 'stbi_write_png_compression_level' (it defaults to 8).\n\n   HDR expects linear float data. Since the format is always 32-bit rgb(e)\n   data, alpha (if provided) is discarded, and for monochrome data it is\n   replicated across all three channels.\n\n   TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed\n   data, set the global variable 'stbi_write_tga_with_rle' to 0.\n   \n   JPEG does ignore alpha channels in input data; quality is between 1 and 100.\n   Higher quality looks better but results in a bigger image.\n   JPEG baseline (no JPEG progressive).\n\nCREDITS:\n\n\n   Sean Barrett           -    PNG/BMP/TGA \n   Baldur Karlsson        -    HDR\n   Jean-Sebastien Guay    -    TGA monochrome\n   Tim Kelsey             -    misc enhancements\n   Alan Hickman           -    TGA RLE\n   Emmanuel Julien        -    initial file IO callback implementation\n   Jon Olick              -    original jo_jpeg.cpp code\n   Daniel Gibson          -    integrate JPEG, allow external zlib\n   Aarni Koskela          -    allow choosing PNG filter\n\n   bugfixes:\n      github:Chribba\n      Guillaume Chereau\n      github:jry2\n      github:romigrou\n      Sergio Gonzalez\n      Jonas Karlsson\n      Filip Wasil\n      Thatcher Ulrich\n      github:poppolopoppo\n      Patrick Boettcher\n      github:xeekworx\n      Cap Petschulat\n      Simon Rodriguez\n      Ivan Tikhonov\n      github:ignotion\n      Adam Schackart\n\nLICENSE\n\n  See end of file for license information.\n\n*/\n\n#ifndef INCLUDE_STB_IMAGE_WRITE_H\n#define INCLUDE_STB_IMAGE_WRITE_H\n\n// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline'\n#ifndef STBIWDEF\n#ifdef STB_IMAGE_WRITE_STATIC\n#define STBIWDEF  static\n#else\n#ifdef __cplusplus\n#define STBIWDEF  extern \"C\"\n#else\n#define STBIWDEF  extern\n#endif\n#endif\n#endif\n\n#ifndef STB_IMAGE_WRITE_STATIC  // C++ forbids static forward declarations\nextern int stbi_write_tga_with_rle;\nextern int stbi_write_png_compression_level;\nextern int stbi_write_force_png_filter;\n#endif\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void  *data, int stride_in_bytes);\nSTBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data);\nSTBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void  *data, int quality);\n#endif\n\ntypedef void stbi_write_func(void *context, void *data, int size);\n\nSTBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data, int stride_in_bytes);\nSTBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void  *data);\nSTBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data);\nSTBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void  *data, int quality);\n\nSTBIWDEF void stbi_flip_vertically_on_write(int flip_boolean);\n\n#endif//INCLUDE_STB_IMAGE_WRITE_H\n\n#ifdef STB_IMAGE_WRITE_IMPLEMENTATION\n\n#ifdef _WIN32\n   #ifndef _CRT_SECURE_NO_WARNINGS\n   #define _CRT_SECURE_NO_WARNINGS\n   #endif\n   #ifndef _CRT_NONSTDC_NO_DEPRECATE\n   #define _CRT_NONSTDC_NO_DEPRECATE\n   #endif\n#endif\n\n#ifndef STBI_WRITE_NO_STDIO\n#include <stdio.h>\n#endif // STBI_WRITE_NO_STDIO\n\n#include <stdarg.h>\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n\n#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED))\n// ok\n#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED)\n// ok\n#else\n#error \"Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED).\"\n#endif\n\n#ifndef STBIW_MALLOC\n#define STBIW_MALLOC(sz)        malloc(sz)\n#define STBIW_REALLOC(p,newsz)  realloc(p,newsz)\n#define STBIW_FREE(p)           free(p)\n#endif\n\n#ifndef STBIW_REALLOC_SIZED\n#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz)\n#endif\n\n\n#ifndef STBIW_MEMMOVE\n#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz)\n#endif\n\n\n#ifndef STBIW_ASSERT\n#include <assert.h>\n#define STBIW_ASSERT(x) assert(x)\n#endif\n\n#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff)\n\n#ifdef STB_IMAGE_WRITE_STATIC\nstatic int stbi__flip_vertically_on_write=0;\nstatic int stbi_write_png_compression_level = 8;\nstatic int stbi_write_tga_with_rle = 1;\nstatic int stbi_write_force_png_filter = -1;\n#else\nint stbi_write_png_compression_level = 8;\nint stbi__flip_vertically_on_write=0;\nint stbi_write_tga_with_rle = 1;\nint stbi_write_force_png_filter = -1;\n#endif\n\nSTBIWDEF void stbi_flip_vertically_on_write(int flag)\n{\n   stbi__flip_vertically_on_write = flag;\n}\n\ntypedef struct\n{\n   stbi_write_func *func;\n   void *context;\n} stbi__write_context;\n\n// initialize a callback-based context\nstatic void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context)\n{\n   s->func    = c;\n   s->context = context;\n}\n\n#ifndef STBI_WRITE_NO_STDIO\n\nstatic void stbi__stdio_write(void *context, void *data, int size)\n{\n   fwrite(data,1,size,(FILE*) context);\n}\n\nstatic int stbi__start_write_file(stbi__write_context *s, const char *filename)\n{\n   FILE *f;\n#ifdef STBI_MSC_SECURE_CRT\n   if (fopen_s(&f, filename, \"wb\"))\n      f = NULL;\n#else\n   f = fopen(filename, \"wb\");\n#endif\n   stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f);\n   return f != NULL;\n}\n\nstatic void stbi__end_write_file(stbi__write_context *s)\n{\n   fclose((FILE *)s->context);\n}\n\n#endif // !STBI_WRITE_NO_STDIO\n\ntypedef unsigned int stbiw_uint32;\ntypedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1];\n\nstatic void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v)\n{\n   while (*fmt) {\n      switch (*fmt++) {\n         case ' ': break;\n         case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int));\n                     s->func(s->context,&x,1);\n                     break; }\n         case '2': { int x = va_arg(v,int);\n                     unsigned char b[2];\n                     b[0] = STBIW_UCHAR(x);\n                     b[1] = STBIW_UCHAR(x>>8);\n                     s->func(s->context,b,2);\n                     break; }\n         case '4': { stbiw_uint32 x = va_arg(v,int);\n                     unsigned char b[4];\n                     b[0]=STBIW_UCHAR(x);\n                     b[1]=STBIW_UCHAR(x>>8);\n                     b[2]=STBIW_UCHAR(x>>16);\n                     b[3]=STBIW_UCHAR(x>>24);\n                     s->func(s->context,b,4);\n                     break; }\n         default:\n            STBIW_ASSERT(0);\n            return;\n      }\n   }\n}\n\nstatic void stbiw__writef(stbi__write_context *s, const char *fmt, ...)\n{\n   va_list v;\n   va_start(v, fmt);\n   stbiw__writefv(s, fmt, v);\n   va_end(v);\n}\n\nstatic void stbiw__putc(stbi__write_context *s, unsigned char c)\n{\n   s->func(s->context, &c, 1);\n}\n\nstatic void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c)\n{\n   unsigned char arr[3];\n   arr[0] = a;\n   arr[1] = b;\n   arr[2] = c;\n   s->func(s->context, arr, 3);\n}\n\nstatic void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d)\n{\n   unsigned char bg[3] = { 255, 0, 255}, px[3];\n   int k;\n\n   if (write_alpha < 0)\n      s->func(s->context, &d[comp - 1], 1);\n\n   switch (comp) {\n      case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case\n      case 1:\n         if (expand_mono)\n            stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp\n         else\n            s->func(s->context, d, 1);  // monochrome TGA\n         break;\n      case 4:\n         if (!write_alpha) {\n            // composite against pink background\n            for (k = 0; k < 3; ++k)\n               px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;\n            stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);\n            break;\n         }\n         /* FALLTHROUGH */\n      case 3:\n         stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);\n         break;\n   }\n   if (write_alpha > 0)\n      s->func(s->context, &d[comp - 1], 1);\n}\n\nstatic void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono)\n{\n   stbiw_uint32 zero = 0;\n   int i,j, j_end;\n\n   if (y <= 0)\n      return;\n\n   if (stbi__flip_vertically_on_write)\n      vdir *= -1;\n\n   if (vdir < 0) {\n      j_end = -1;\n      j = y-1;\n   } else {\n      j_end = y;\n      j = 0;\n   }\n\n   for (; j != j_end; j += vdir) {\n      for (i=0; i < x; ++i) {\n         unsigned char *d = (unsigned char *) data + (j*x+i)*comp;\n         stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);\n      }\n      s->func(s->context, &zero, scanline_pad);\n   }\n}\n\nstatic int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...)\n{\n   if (y < 0 || x < 0) {\n      return 0;\n   } else {\n      va_list v;\n      va_start(v, fmt);\n      stbiw__writefv(s, fmt, v);\n      va_end(v);\n      stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono);\n      return 1;\n   }\n}\n\nstatic int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data)\n{\n   int pad = (-x*3) & 3;\n   return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad,\n           \"11 4 22 4\" \"4 44 22 444444\",\n           'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40,  // file header\n            40, x,y, 1,24, 0,0,0,0,0,0);             // bitmap header\n}\n\nSTBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s;\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_bmp_core(&s, x, y, comp, data);\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s;\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_bmp_core(&s, x, y, comp, data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif //!STBI_WRITE_NO_STDIO\n\nstatic int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data)\n{\n   int has_alpha = (comp == 2 || comp == 4);\n   int colorbytes = has_alpha ? comp-1 : comp;\n   int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3\n\n   if (y < 0 || x < 0)\n      return 0;\n\n   if (!stbi_write_tga_with_rle) {\n      return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0,\n         \"111 221 2222 11\", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8);\n   } else {\n      int i,j,k;\n      int jend, jdir;\n\n      stbiw__writef(s, \"111 221 2222 11\", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8);\n\n      if (stbi__flip_vertically_on_write) {\n         j = 0;\n         jend = y;\n         jdir = 1;\n      } else {\n         j = y-1;\n         jend = -1;\n         jdir = -1;\n      }\n      for (; j != jend; j += jdir) {\n         unsigned char *row = (unsigned char *) data + j * x * comp;\n         int len;\n\n         for (i = 0; i < x; i += len) {\n            unsigned char *begin = row + i * comp;\n            int diff = 1;\n            len = 1;\n\n            if (i < x - 1) {\n               ++len;\n               diff = memcmp(begin, row + (i + 1) * comp, comp);\n               if (diff) {\n                  const unsigned char *prev = begin;\n                  for (k = i + 2; k < x && len < 128; ++k) {\n                     if (memcmp(prev, row + k * comp, comp)) {\n                        prev += comp;\n                        ++len;\n                     } else {\n                        --len;\n                        break;\n                     }\n                  }\n               } else {\n                  for (k = i + 2; k < x && len < 128; ++k) {\n                     if (!memcmp(begin, row + k * comp, comp)) {\n                        ++len;\n                     } else {\n                        break;\n                     }\n                  }\n               }\n            }\n\n            if (diff) {\n               unsigned char header = STBIW_UCHAR(len - 1);\n               s->func(s->context, &header, 1);\n               for (k = 0; k < len; ++k) {\n                  stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);\n               }\n            } else {\n               unsigned char header = STBIW_UCHAR(len - 129);\n               s->func(s->context, &header, 1);\n               stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);\n            }\n         }\n      }\n   }\n   return 1;\n}\n\nSTBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s;\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_tga_core(&s, x, y, comp, (void *) data);\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data)\n{\n   stbi__write_context s;\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_tga_core(&s, x, y, comp, (void *) data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif\n\n// *************************************************************************************************\n// Radiance RGBE HDR writer\n// by Baldur Karlsson\n\n#define stbiw__max(a, b)  ((a) > (b) ? (a) : (b))\n\nvoid stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear)\n{\n   int exponent;\n   float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2]));\n\n   if (maxcomp < 1e-32f) {\n      rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;\n   } else {\n      float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp;\n\n      rgbe[0] = (unsigned char)(linear[0] * normalize);\n      rgbe[1] = (unsigned char)(linear[1] * normalize);\n      rgbe[2] = (unsigned char)(linear[2] * normalize);\n      rgbe[3] = (unsigned char)(exponent + 128);\n   }\n}\n\nvoid stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte)\n{\n   unsigned char lengthbyte = STBIW_UCHAR(length+128);\n   STBIW_ASSERT(length+128 <= 255);\n   s->func(s->context, &lengthbyte, 1);\n   s->func(s->context, &databyte, 1);\n}\n\nvoid stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data)\n{\n   unsigned char lengthbyte = STBIW_UCHAR(length);\n   STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code\n   s->func(s->context, &lengthbyte, 1);\n   s->func(s->context, data, length);\n}\n\nvoid stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline)\n{\n   unsigned char scanlineheader[4] = { 2, 2, 0, 0 };\n   unsigned char rgbe[4];\n   float linear[3];\n   int x;\n\n   scanlineheader[2] = (width&0xff00)>>8;\n   scanlineheader[3] = (width&0x00ff);\n\n   /* skip RLE for images too small or large */\n   if (width < 8 || width >= 32768) {\n      for (x=0; x < width; x++) {\n         switch (ncomp) {\n            case 4: /* fallthrough */\n            case 3: linear[2] = scanline[x*ncomp + 2];\n                    linear[1] = scanline[x*ncomp + 1];\n                    linear[0] = scanline[x*ncomp + 0];\n                    break;\n            default:\n                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];\n                    break;\n         }\n         stbiw__linear_to_rgbe(rgbe, linear);\n         s->func(s->context, rgbe, 4);\n      }\n   } else {\n      int c,r;\n      /* encode into scratch buffer */\n      for (x=0; x < width; x++) {\n         switch(ncomp) {\n            case 4: /* fallthrough */\n            case 3: linear[2] = scanline[x*ncomp + 2];\n                    linear[1] = scanline[x*ncomp + 1];\n                    linear[0] = scanline[x*ncomp + 0];\n                    break;\n            default:\n                    linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0];\n                    break;\n         }\n         stbiw__linear_to_rgbe(rgbe, linear);\n         scratch[x + width*0] = rgbe[0];\n         scratch[x + width*1] = rgbe[1];\n         scratch[x + width*2] = rgbe[2];\n         scratch[x + width*3] = rgbe[3];\n      }\n\n      s->func(s->context, scanlineheader, 4);\n\n      /* RLE each component separately */\n      for (c=0; c < 4; c++) {\n         unsigned char *comp = &scratch[width*c];\n\n         x = 0;\n         while (x < width) {\n            // find first run\n            r = x;\n            while (r+2 < width) {\n               if (comp[r] == comp[r+1] && comp[r] == comp[r+2])\n                  break;\n               ++r;\n            }\n            if (r+2 >= width)\n               r = width;\n            // dump up to first run\n            while (x < r) {\n               int len = r-x;\n               if (len > 128) len = 128;\n               stbiw__write_dump_data(s, len, &comp[x]);\n               x += len;\n            }\n            // if there's a run, output it\n            if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd\n               // find next byte after run\n               while (r < width && comp[r] == comp[x])\n                  ++r;\n               // output run up to r\n               while (x < r) {\n                  int len = r-x;\n                  if (len > 127) len = 127;\n                  stbiw__write_run_data(s, len, comp[x]);\n                  x += len;\n               }\n            }\n         }\n      }\n   }\n}\n\nstatic int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data)\n{\n   if (y <= 0 || x <= 0 || data == NULL)\n      return 0;\n   else {\n      // Each component is stored separately. Allocate scratch space for full output scanline.\n      unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4);\n      int i, len;\n      char buffer[128];\n      char header[] = \"#?RADIANCE\\n# Written by stb_image_write.h\\nFORMAT=32-bit_rle_rgbe\\n\";\n      s->func(s->context, header, sizeof(header)-1);\n\n#ifdef STBI_MSC_SECURE_CRT\n      len = sprintf_s(buffer, \"EXPOSURE=          1.0000000000000\\n\\n-Y %d +X %d\\n\", y, x);\n#else\n      len = sprintf(buffer, \"EXPOSURE=          1.0000000000000\\n\\n-Y %d +X %d\\n\", y, x);\n#endif\n      s->func(s->context, buffer, len);\n\n      for(i=0; i < y; i++)\n         stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)*x);\n      STBIW_FREE(scratch);\n      return 1;\n   }\n}\n\nSTBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data)\n{\n   stbi__write_context s;\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_hdr_core(&s, x, y, comp, (float *) data);\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data)\n{\n   stbi__write_context s;\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif // STBI_WRITE_NO_STDIO\n\n\n//////////////////////////////////////////////////////////////////////////////\n//\n// PNG writer\n//\n\n#ifndef STBIW_ZLIB_COMPRESS\n// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size()\n#define stbiw__sbraw(a) ((int *) (a) - 2)\n#define stbiw__sbm(a)   stbiw__sbraw(a)[0]\n#define stbiw__sbn(a)   stbiw__sbraw(a)[1]\n\n#define stbiw__sbneedgrow(a,n)  ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a))\n#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0)\n#define stbiw__sbgrow(a,n)  stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a)))\n\n#define stbiw__sbpush(a, v)      (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v))\n#define stbiw__sbcount(a)        ((a) ? stbiw__sbn(a) : 0)\n#define stbiw__sbfree(a)         ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0)\n\nstatic void *stbiw__sbgrowf(void **arr, int increment, int itemsize)\n{\n   int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1;\n   void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2);\n   STBIW_ASSERT(p);\n   if (p) {\n      if (!*arr) ((int *) p)[1] = 0;\n      *arr = (void *) ((int *) p + 2);\n      stbiw__sbm(*arr) = m;\n   }\n   return *arr;\n}\n\nstatic unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount)\n{\n   while (*bitcount >= 8) {\n      stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer));\n      *bitbuffer >>= 8;\n      *bitcount -= 8;\n   }\n   return data;\n}\n\nstatic int stbiw__zlib_bitrev(int code, int codebits)\n{\n   int res=0;\n   while (codebits--) {\n      res = (res << 1) | (code & 1);\n      code >>= 1;\n   }\n   return res;\n}\n\nstatic unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit)\n{\n   int i;\n   for (i=0; i < limit && i < 258; ++i)\n      if (a[i] != b[i]) break;\n   return i;\n}\n\nstatic unsigned int stbiw__zhash(unsigned char *data)\n{\n   stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16);\n   hash ^= hash << 3;\n   hash += hash >> 5;\n   hash ^= hash << 4;\n   hash += hash >> 17;\n   hash ^= hash << 25;\n   hash += hash >> 6;\n   return hash;\n}\n\n#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount))\n#define stbiw__zlib_add(code,codebits) \\\n      (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush())\n#define stbiw__zlib_huffa(b,c)  stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c)\n// default huffman tables\n#define stbiw__zlib_huff1(n)  stbiw__zlib_huffa(0x30 + (n), 8)\n#define stbiw__zlib_huff2(n)  stbiw__zlib_huffa(0x190 + (n)-144, 9)\n#define stbiw__zlib_huff3(n)  stbiw__zlib_huffa(0 + (n)-256,7)\n#define stbiw__zlib_huff4(n)  stbiw__zlib_huffa(0xc0 + (n)-280,8)\n#define stbiw__zlib_huff(n)  ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n))\n#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n))\n\n#define stbiw__ZHASH   16384\n\n#endif // STBIW_ZLIB_COMPRESS\n\nunsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality)\n{\n#ifdef STBIW_ZLIB_COMPRESS\n   // user provided a zlib compress implementation, use that\n   return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality);\n#else // use builtin\n   static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 };\n   static unsigned char  lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4,  4,  5,  5,  5,  5,  0 };\n   static unsigned short distc[]   = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 };\n   static unsigned char  disteb[]  = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 };\n   unsigned int bitbuf=0;\n   int i,j, bitcount=0;\n   unsigned char *out = NULL;\n   unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**));\n   if (hash_table == NULL)\n      return NULL;\n   if (quality < 5) quality = 5;\n\n   stbiw__sbpush(out, 0x78);   // DEFLATE 32K window\n   stbiw__sbpush(out, 0x5e);   // FLEVEL = 1\n   stbiw__zlib_add(1,1);  // BFINAL = 1\n   stbiw__zlib_add(1,2);  // BTYPE = 1 -- fixed huffman\n\n   for (i=0; i < stbiw__ZHASH; ++i)\n      hash_table[i] = NULL;\n\n   i=0;\n   while (i < data_len-3) {\n      // hash next 3 bytes of data to be compressed\n      int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3;\n      unsigned char *bestloc = 0;\n      unsigned char **hlist = hash_table[h];\n      int n = stbiw__sbcount(hlist);\n      for (j=0; j < n; ++j) {\n         if (hlist[j]-data > i-32768) { // if entry lies within window\n            int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i);\n            if (d >= best) {\n               best=d; bestloc=hlist[j];\n            }\n         }\n      }\n      // when hash table entry is too long, delete half the entries\n      if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) {\n         STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality);\n         stbiw__sbn(hash_table[h]) = quality;\n      }\n      stbiw__sbpush(hash_table[h],data+i);\n\n      if (bestloc) {\n         // \"lazy matching\" - check match at *next* byte, and if it's better, do cur byte as literal\n         h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1);\n         hlist = hash_table[h];\n         n = stbiw__sbcount(hlist);\n         for (j=0; j < n; ++j) {\n            if (hlist[j]-data > i-32767) {\n               int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1);\n               if (e > best) { // if next match is better, bail on current match\n                  bestloc = NULL;\n                  break;\n               }\n            }\n         }\n      }\n\n      if (bestloc) {\n         int d = (int) (data+i - bestloc); // distance back\n         STBIW_ASSERT(d <= 32767 && best <= 258);\n         for (j=0; best > lengthc[j+1]-1; ++j);\n         stbiw__zlib_huff(j+257);\n         if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]);\n         for (j=0; d > distc[j+1]-1; ++j);\n         stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5);\n         if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]);\n         i += best;\n      } else {\n         stbiw__zlib_huffb(data[i]);\n         ++i;\n      }\n   }\n   // write out final bytes\n   for (;i < data_len; ++i)\n      stbiw__zlib_huffb(data[i]);\n   stbiw__zlib_huff(256); // end of block\n   // pad with 0 bits to byte boundary\n   while (bitcount)\n      stbiw__zlib_add(0,1);\n\n   for (i=0; i < stbiw__ZHASH; ++i)\n      (void) stbiw__sbfree(hash_table[i]);\n   STBIW_FREE(hash_table);\n\n   {\n      // compute adler32 on input\n      unsigned int s1=1, s2=0;\n      int blocklen = (int) (data_len % 5552);\n      j=0;\n      while (j < data_len) {\n         for (i=0; i < blocklen; ++i) {\n            s1 += data[j+i];\n            s2 += s1;\n         }\n         s1 %= 65521;\n         s2 %= 65521;\n         j += blocklen;\n         blocklen = 5552;\n      }\n      stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8));\n      stbiw__sbpush(out, STBIW_UCHAR(s2));\n      stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8));\n      stbiw__sbpush(out, STBIW_UCHAR(s1));\n   }\n   *out_len = stbiw__sbn(out);\n   // make returned pointer freeable\n   STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len);\n   return (unsigned char *) stbiw__sbraw(out);\n#endif // STBIW_ZLIB_COMPRESS\n}\n\nstatic unsigned int stbiw__crc32(unsigned char *buffer, int len)\n{\n   static unsigned int crc_table[256] =\n   {\n      0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,\n      0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,\n      0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,\n      0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,\n      0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,\n      0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,\n      0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,\n      0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,\n      0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,\n      0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,\n      0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,\n      0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,\n      0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,\n      0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,\n      0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,\n      0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,\n      0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,\n      0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,\n      0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,\n      0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,\n      0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,\n      0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,\n      0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,\n      0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,\n      0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,\n      0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,\n      0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,\n      0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,\n      0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,\n      0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,\n      0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,\n      0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D\n   };\n\n   unsigned int crc = ~0u;\n   int i;\n   for (i=0; i < len; ++i)\n      crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];\n   return ~crc;\n}\n\n#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4)\n#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v));\n#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3])\n\nstatic void stbiw__wpcrc(unsigned char **data, int len)\n{\n   unsigned int crc = stbiw__crc32(*data - len - 4, len+4);\n   stbiw__wp32(*data, crc);\n}\n\nstatic unsigned char stbiw__paeth(int a, int b, int c)\n{\n   int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c);\n   if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);\n   if (pb <= pc) return STBIW_UCHAR(b);\n   return STBIW_UCHAR(c);\n}\n\n// @OPTIMIZE: provide an option that always forces left-predict or paeth predict\nstatic void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer)\n{\n   static int mapping[] = { 0,1,2,3,4 };\n   static int firstmap[] = { 0,1,0,5,6 };\n   int *mymap = (y != 0) ? mapping : firstmap;\n   int i;\n   int type = mymap[filter_type];\n   unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y);\n   int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes;\n   for (i = 0; i < n; ++i) {\n      switch (type) {\n         case 0: line_buffer[i] = z[i]; break;\n         case 1: line_buffer[i] = z[i]; break;\n         case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;\n         case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break;\n         case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break;\n         case 5: line_buffer[i] = z[i]; break;\n         case 6: line_buffer[i] = z[i]; break;\n      }\n   }\n   for (i=n; i < width*n; ++i) {\n      switch (type) {\n         case 0: line_buffer[i] = z[i]; break;\n         case 1: line_buffer[i] = z[i] - z[i-n]; break;\n         case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break;\n         case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break;\n         case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break;\n         case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break;\n         case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break;\n      }\n   }\n}\n\nunsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len)\n{\n   int force_filter = stbi_write_force_png_filter;\n   int ctype[5] = { -1, 0, 4, 2, 6 };\n   unsigned char sig[8] = { 137,80,78,71,13,10,26,10 };\n   unsigned char *out,*o, *filt, *zlib;\n   signed char *line_buffer;\n   int j,zlen;\n\n   if (stride_bytes == 0)\n      stride_bytes = x * n;\n\n   if (force_filter >= 5) {\n      force_filter = -1;\n   }\n\n   filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0;\n   line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; }\n   for (j=0; j < y; ++j) {\n      int filter_type;\n      if (force_filter > -1) {\n         filter_type = force_filter;\n         stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter, line_buffer);\n      } else { // Estimate the best filter by running through all of them:\n         int best_filter = 0, best_filter_val = 0x7fffffff, est, i;\n         for (filter_type = 0; filter_type < 5; filter_type++) {\n            stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type, line_buffer);\n\n            // Estimate the entropy of the line using this filter; the less, the better.\n            est = 0;\n            for (i = 0; i < x*n; ++i) {\n               est += abs((signed char) line_buffer[i]);\n            }\n            if (est < best_filter_val) {\n               best_filter_val = est;\n               best_filter = filter_type;\n            }\n         }\n         if (filter_type != best_filter) {  // If the last iteration already got us the best filter, don't redo it\n            stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter, line_buffer);\n            filter_type = best_filter;\n         }\n      }\n      // when we get here, filter_type contains the filter type, and line_buffer contains the data\n      filt[j*(x*n+1)] = (unsigned char) filter_type;\n      STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n);\n   }\n   STBIW_FREE(line_buffer);\n   zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level);\n   STBIW_FREE(filt);\n   if (!zlib) return 0;\n\n   // each tag requires 12 bytes of overhead\n   out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12);\n   if (!out) return 0;\n   *out_len = 8 + 12+13 + 12+zlen + 12;\n\n   o=out;\n   STBIW_MEMMOVE(o,sig,8); o+= 8;\n   stbiw__wp32(o, 13); // header length\n   stbiw__wptag(o, \"IHDR\");\n   stbiw__wp32(o, x);\n   stbiw__wp32(o, y);\n   *o++ = 8;\n   *o++ = STBIW_UCHAR(ctype[n]);\n   *o++ = 0;\n   *o++ = 0;\n   *o++ = 0;\n   stbiw__wpcrc(&o,13);\n\n   stbiw__wp32(o, zlen);\n   stbiw__wptag(o, \"IDAT\");\n   STBIW_MEMMOVE(o, zlib, zlen);\n   o += zlen;\n   STBIW_FREE(zlib);\n   stbiw__wpcrc(&o, zlen);\n\n   stbiw__wp32(o,0);\n   stbiw__wptag(o, \"IEND\");\n   stbiw__wpcrc(&o,0);\n\n   STBIW_ASSERT(o == out + *out_len);\n\n   return out;\n}\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)\n{\n   FILE *f;\n   int len;\n   unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);\n   if (png == NULL) return 0;\n#ifdef STBI_MSC_SECURE_CRT\n   if (fopen_s(&f, filename, \"wb\"))\n      f = NULL;\n#else\n   f = fopen(filename, \"wb\");\n#endif\n   if (!f) { STBIW_FREE(png); return 0; }\n   fwrite(png, 1, len, f);\n   fclose(f);\n   STBIW_FREE(png);\n   return 1;\n}\n#endif\n\nSTBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)\n{\n   int len;\n   unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len);\n   if (png == NULL) return 0;\n   func(context, png, len);\n   STBIW_FREE(png);\n   return 1;\n}\n\n\n/* ***************************************************************************\n *\n * JPEG writer\n *\n * This is based on Jon Olick's jo_jpeg.cpp:\n * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html\n */\n\nstatic const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,\n      24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 };\n\nstatic void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) {\n   int bitBuf = *bitBufP, bitCnt = *bitCntP;\n   bitCnt += bs[1];\n   bitBuf |= bs[0] << (24 - bitCnt);\n   while(bitCnt >= 8) {\n      unsigned char c = (bitBuf >> 16) & 255;\n      stbiw__putc(s, c);\n      if(c == 255) {\n         stbiw__putc(s, 0);\n      }\n      bitBuf <<= 8;\n      bitCnt -= 8;\n   }\n   *bitBufP = bitBuf;\n   *bitCntP = bitCnt;\n}\n\nstatic void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) {\n   float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p;\n   float z1, z2, z3, z4, z5, z11, z13;\n\n   float tmp0 = d0 + d7;\n   float tmp7 = d0 - d7;\n   float tmp1 = d1 + d6;\n   float tmp6 = d1 - d6;\n   float tmp2 = d2 + d5;\n   float tmp5 = d2 - d5;\n   float tmp3 = d3 + d4;\n   float tmp4 = d3 - d4;\n\n   // Even part\n   float tmp10 = tmp0 + tmp3;   // phase 2\n   float tmp13 = tmp0 - tmp3;\n   float tmp11 = tmp1 + tmp2;\n   float tmp12 = tmp1 - tmp2;\n\n   d0 = tmp10 + tmp11;       // phase 3\n   d4 = tmp10 - tmp11;\n\n   z1 = (tmp12 + tmp13) * 0.707106781f; // c4\n   d2 = tmp13 + z1;       // phase 5\n   d6 = tmp13 - z1;\n\n   // Odd part\n   tmp10 = tmp4 + tmp5;       // phase 2\n   tmp11 = tmp5 + tmp6;\n   tmp12 = tmp6 + tmp7;\n\n   // The rotator is modified from fig 4-8 to avoid extra negations.\n   z5 = (tmp10 - tmp12) * 0.382683433f; // c6\n   z2 = tmp10 * 0.541196100f + z5; // c2-c6\n   z4 = tmp12 * 1.306562965f + z5; // c2+c6\n   z3 = tmp11 * 0.707106781f; // c4\n\n   z11 = tmp7 + z3;      // phase 5\n   z13 = tmp7 - z3;\n\n   *d5p = z13 + z2;         // phase 6\n   *d3p = z13 - z2;\n   *d1p = z11 + z4;\n   *d7p = z11 - z4;\n\n   *d0p = d0;  *d2p = d2;  *d4p = d4;  *d6p = d6;\n}\n\nstatic void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {\n   int tmp1 = val < 0 ? -val : val;\n   val = val < 0 ? val-1 : val;\n   bits[1] = 1;\n   while(tmp1 >>= 1) {\n      ++bits[1];\n   }\n   bits[0] = val & ((1<<bits[1])-1);\n}\n\nstatic int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, int *bitCnt, float *CDU, float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) {\n   const unsigned short EOB[2] = { HTAC[0x00][0], HTAC[0x00][1] };\n   const unsigned short M16zeroes[2] = { HTAC[0xF0][0], HTAC[0xF0][1] };\n   int dataOff, i, diff, end0pos;\n   int DU[64];\n\n   // DCT rows\n   for(dataOff=0; dataOff<64; dataOff+=8) {\n      stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+1], &CDU[dataOff+2], &CDU[dataOff+3], &CDU[dataOff+4], &CDU[dataOff+5], &CDU[dataOff+6], &CDU[dataOff+7]);\n   }\n   // DCT columns\n   for(dataOff=0; dataOff<8; ++dataOff) {\n      stbiw__jpg_DCT(&CDU[dataOff], &CDU[dataOff+8], &CDU[dataOff+16], &CDU[dataOff+24], &CDU[dataOff+32], &CDU[dataOff+40], &CDU[dataOff+48], &CDU[dataOff+56]);\n   }\n   // Quantize/descale/zigzag the coefficients\n   for(i=0; i<64; ++i) {\n      float v = CDU[i]*fdtbl[i];\n      // DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f));\n      // ceilf() and floorf() are C99, not C89, but I /think/ they're not needed here anyway?\n      DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f);\n   }\n\n   // Encode DC\n   diff = DU[0] - DC;\n   if (diff == 0) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]);\n   } else {\n      unsigned short bits[2];\n      stbiw__jpg_calcBits(diff, bits);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);\n   }\n   // Encode ACs\n   end0pos = 63;\n   for(; (end0pos>0)&&(DU[end0pos]==0); --end0pos) {\n   }\n   // end0pos = first element in reverse order !=0\n   if(end0pos == 0) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);\n      return DU[0];\n   }\n   for(i = 1; i <= end0pos; ++i) {\n      int startpos = i;\n      int nrzeroes;\n      unsigned short bits[2];\n      for (; DU[i]==0 && i<=end0pos; ++i) {\n      }\n      nrzeroes = i-startpos;\n      if ( nrzeroes >= 16 ) {\n         int lng = nrzeroes>>4;\n         int nrmarker;\n         for (nrmarker=1; nrmarker <= lng; ++nrmarker)\n            stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);\n         nrzeroes &= 15;\n      }\n      stbiw__jpg_calcBits(DU[i], bits);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]);\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);\n   }\n   if(end0pos != 63) {\n      stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);\n   }\n   return DU[0];\n}\n\nstatic int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) {\n   // Constants that don't pollute global namespace\n   static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0};\n   static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};\n   static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d};\n   static const unsigned char std_ac_luminance_values[] = {\n      0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,\n      0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,\n      0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,\n      0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,\n      0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,\n      0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,\n      0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa\n   };\n   static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0};\n   static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11};\n   static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77};\n   static const unsigned char std_ac_chrominance_values[] = {\n      0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,\n      0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,\n      0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,\n      0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,\n      0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,\n      0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,\n      0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa\n   };\n   // Huffman tables\n   static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}};\n   static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}};\n   static const unsigned short YAC_HT[256][2] = {\n      {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}\n   };\n   static const unsigned short UVAC_HT[256][2] = {\n      {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0},\n      {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0}\n   };\n   static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22,\n                             37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99};\n   static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99,\n                              99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99};\n   static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, \n                                 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f };\n\n   int row, col, i, k;\n   float fdtbl_Y[64], fdtbl_UV[64];\n   unsigned char YTable[64], UVTable[64];\n\n   if(!data || !width || !height || comp > 4 || comp < 1) {\n      return 0;\n   }\n\n   quality = quality ? quality : 90;\n   quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;\n   quality = quality < 50 ? 5000 / quality : 200 - quality * 2;\n\n   for(i = 0; i < 64; ++i) {\n      int uvti, yti = (YQT[i]*quality+50)/100;\n      YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti);\n      uvti = (UVQT[i]*quality+50)/100;\n      UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti);\n   }\n\n   for(row = 0, k = 0; row < 8; ++row) {\n      for(col = 0; col < 8; ++col, ++k) {\n         fdtbl_Y[k]  = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);\n         fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);\n      }\n   }\n\n   // Write Headers\n   {\n      static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 };\n      static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 };\n      const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width),\n                                      3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 };\n      s->func(s->context, (void*)head0, sizeof(head0));\n      s->func(s->context, (void*)YTable, sizeof(YTable));\n      stbiw__putc(s, 1);\n      s->func(s->context, UVTable, sizeof(UVTable));\n      s->func(s->context, (void*)head1, sizeof(head1));\n      s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1);\n      s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values));\n      stbiw__putc(s, 0x10); // HTYACinfo\n      s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1);\n      s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values));\n      stbiw__putc(s, 1); // HTUDCinfo\n      s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1);\n      s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values));\n      stbiw__putc(s, 0x11); // HTUACinfo\n      s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1);\n      s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values));\n      s->func(s->context, (void*)head2, sizeof(head2));\n   }\n\n   // Encode 8x8 macroblocks\n   {\n      static const unsigned short fillBits[] = {0x7F, 7};\n      const unsigned char *imageData = (const unsigned char *)data;\n      int DCY=0, DCU=0, DCV=0;\n      int bitBuf=0, bitCnt=0;\n      // comp == 2 is grey+alpha (alpha is ignored)\n      int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;\n      int x, y, pos;\n      for(y = 0; y < height; y += 8) {\n         for(x = 0; x < width; x += 8) {\n            float YDU[64], UDU[64], VDU[64];\n            for(row = y, pos = 0; row < y+8; ++row) {\n               for(col = x; col < x+8; ++col, ++pos) {\n                  int p = (stbi__flip_vertically_on_write ? height-1-row : row)*width*comp + col*comp;\n                  float r, g, b;\n                  if(row >= height) {\n                     p -= width*comp*(row+1 - height);\n                  }\n                  if(col >= width) {\n                     p -= comp*(col+1 - width);\n                  }\n\n                  r = imageData[p+0];\n                  g = imageData[p+ofsG];\n                  b = imageData[p+ofsB];\n                  YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128;\n                  UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b;\n                  VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b;\n               }\n            }\n\n            DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT);\n            DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT);\n            DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT);\n         }\n      }\n\n      // Do the bit alignment of the EOI marker\n      stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);\n   }\n\n   // EOI\n   stbiw__putc(s, 0xFF);\n   stbiw__putc(s, 0xD9);\n\n   return 1;\n}\n\nSTBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality)\n{\n   stbi__write_context s;\n   stbi__start_write_callbacks(&s, func, context);\n   return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality);\n}\n\n\n#ifndef STBI_WRITE_NO_STDIO\nSTBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality)\n{\n   stbi__write_context s;\n   if (stbi__start_write_file(&s,filename)) {\n      int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);\n      stbi__end_write_file(&s);\n      return r;\n   } else\n      return 0;\n}\n#endif\n\n#endif // STB_IMAGE_WRITE_IMPLEMENTATION\n\n/* Revision history\n      1.09  (2018-02-11)\n             fix typo in zlib quality API, improve STB_I_W_STATIC in C++\n      1.08  (2018-01-29)\n             add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter\n      1.07  (2017-07-24)\n             doc fix\n      1.06 (2017-07-23)\n             writing JPEG (using Jon Olick's code)\n      1.05   ???\n      1.04 (2017-03-03)\n             monochrome BMP expansion\n      1.03   ???\n      1.02 (2016-04-02)\n             avoid allocating large structures on the stack\n      1.01 (2016-01-16)\n             STBIW_REALLOC_SIZED: support allocators with no realloc support\n             avoid race-condition in crc initialization\n             minor compile issues\n      1.00 (2015-09-14)\n             installable file IO function\n      0.99 (2015-09-13)\n             warning fixes; TGA rle support\n      0.98 (2015-04-08)\n             added STBIW_MALLOC, STBIW_ASSERT etc\n      0.97 (2015-01-18)\n             fixed HDR asserts, rewrote HDR rle logic\n      0.96 (2015-01-17)\n             add HDR output\n             fix monochrome BMP\n      0.95 (2014-08-17)\n\t\t       add monochrome TGA output\n      0.94 (2014-05-31)\n             rename private functions to avoid conflicts with stb_image.h\n      0.93 (2014-05-27)\n             warning fixes\n      0.92 (2010-08-01)\n             casts to unsigned char to fix warnings\n      0.91 (2010-07-17)\n             first public release\n      0.90   first internal release\n*/\n\n/*\n------------------------------------------------------------------------------\nThis software is available under 2 licenses -- choose whichever you prefer.\n------------------------------------------------------------------------------\nALTERNATIVE A - MIT License\nCopyright (c) 2017 Sean Barrett\nPermission is hereby granted, free of charge, to any person obtaining a copy of \nthis software and associated documentation files (the \"Software\"), to deal in \nthe Software without restriction, including without limitation the rights to \nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies \nof the Software, and to permit persons to whom the Software is furnished to do \nso, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all \ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR \nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE \nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE \nSOFTWARE.\n------------------------------------------------------------------------------\nALTERNATIVE B - Public Domain (www.unlicense.org)\nThis is free and unencumbered software released into the public domain.\nAnyone is free to copy, modify, publish, use, compile, sell, or distribute this \nsoftware, either in source code form or as a compiled binary, for any purpose, \ncommercial or non-commercial, and by any means.\nIn jurisdictions that recognize copyright laws, the author or authors of this \nsoftware dedicate any and all copyright interest in the software to the public \ndomain. We make this dedication for the benefit of the public at large and to \nthe detriment of our heirs and successors. We intend this dedication to be an \novert act of relinquishment in perpetuity of all present and future rights to \nthis software under copyright law.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR \nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE \nAUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN \nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION \nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n------------------------------------------------------------------------------\n*/\n"
  },
  {
    "path": "sdl/system_paths.c",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"system_paths.h\"\n#include <stdlib.h>\n#include <string.h>\n\n#if defined(_WIN32)\n#include <windows.h>\n#endif\n\nvoid desktop_path(char *buffer, size_t size)\n{\n#if defined(__APPLE__) && defined(__MACH__)\n    strncpy(buffer, getenv(\"HOME\"), size - 1);\n    strncat(buffer, \"/Desktop/\", size - 1);\n#elif defined(_WIN32)\n\tstrncpy(buffer, getenv(\"USERPROFILE\"), size - 1);\n\tstrncat(buffer, \"\\\\Desktop\\\\\", size - 1);\n#elif defined(__EMSCRIPTEN__)\n    strncpy(buffer, \"\", size - 1);\n#elif defined(__LINUX__)\n    strncpy(buffer, getenv(\"HOME\"), size - 1);\n    strncat(buffer, \"/Desktop/\", size - 1);\n#else\n#error Not implemented yet\n#endif\n}\n\nFILE* fopen_utf8(const char* filename, const char* mode)\n{\n#if defined(_WIN32)\n\tWCHAR nameW[FILENAME_MAX] = { 0 };\n\tWCHAR modeW[16] = { 0 };\n\tint len = MultiByteToWideChar(CP_UTF8, 0, filename, -1, nameW, FILENAME_MAX);\n\tif (len > 0 && MultiByteToWideChar(CP_UTF8, 0, mode, -1, modeW, 16) > 0)\n\t{\n\t\tFILE* ret = NULL;\n\t\tif (_wfopen_s(&ret, nameW, modeW) == 0)\n\t\t\treturn ret;\n\t}\n\treturn NULL;\n#else\n\treturn fopen(filename, mode);\n#endif\n}\n"
  },
  {
    "path": "sdl/system_paths.h",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef system_paths_h\n#define system_paths_h\n\n#include <stdio.h>\n\n#ifdef _WIN32\n#define PATH_SEPARATOR \"\\\\\"\n#define PATH_SEPARATOR_CHAR '\\\\'\n#else\n#define PATH_SEPARATOR \"/\"\n#define PATH_SEPARATOR_CHAR '/'\n#endif\n\nvoid desktop_path(char *buffer, size_t size);\nFILE* fopen_utf8(const char* filename, const char* mode);\n\n#endif /* system_paths_h */\n"
  },
  {
    "path": "sdl/utils.c",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#include \"utils.h\"\n#include \"system_paths.h\"\n#include <string.h>\n\nvoid displayName(const char *filename, char *destination, size_t size)\n{\n    memset(destination, 0, size);\n    \n    const char *nameStart = filename;\n    char *slash = strrchr(filename, PATH_SEPARATOR_CHAR);\n    if (slash)\n    {\n        nameStart = slash + 1;\n    }\n    strncpy(destination, nameStart, size - 1);\n    \n    char *dot = strrchr(nameStart, '.');\n    if (dot)\n    {\n        int dotIndex = (int)(dot - nameStart);\n        if (dotIndex < size)\n        {\n            destination[dotIndex] = 0;\n        }\n    }\n}\n\nbool hasPostfix(const char *string, const char *postfix)\n{\n    size_t stringLen = strlen(string);\n    size_t postfixLen = strlen(postfix);\n    if (postfixLen <= stringLen)\n    {\n        string = string + stringLen - postfixLen;\n        return strcmp(string, postfix) == 0;\n    }\n    return false;\n}\n"
  },
  {
    "path": "sdl/utils.h",
    "content": "//\n// Copyright 2018 Timo Kloss\n//\n// This software is provided 'as-is', without any express or implied\n// warranty. In no event will the authors be held liable for any damages\n// arising from the use of this software.\n//\n// Permission is granted to anyone to use this software for any purpose,\n// including commercial applications, and to alter it and redistribute it\n// freely, subject to the following restrictions:\n//\n// 1. The origin of this software must not be misrepresented; you must not\n//    claim that you wrote the original software. If you use this software\n//    in a product, an acknowledgment in the product documentation would be\n//    appreciated but is not required.\n// 2. Altered source versions must be plainly marked as such, and must not be\n//    misrepresented as being the original software.\n// 3. This notice may not be removed or altered from any source distribution.\n//\n\n#ifndef utils_h\n#define utils_h\n\n#include <stdio.h>\n#include <stdbool.h>\n\nvoid displayName(const char *filename, char *destination, size_t size);\nbool hasPostfix(const char *string, const char *postfix);\n\n#endif /* utils_h */\n"
  }
]